diff --git a/Examples/Plugins/org.mitk.example.gui.imaging/src/internal/colourimageprocessing/QmitkColourImageProcessingView.cpp b/Examples/Plugins/org.mitk.example.gui.imaging/src/internal/colourimageprocessing/QmitkColourImageProcessingView.cpp index 3e1fdcaa98..641ddd8869 100644 --- a/Examples/Plugins/org.mitk.example.gui.imaging/src/internal/colourimageprocessing/QmitkColourImageProcessingView.cpp +++ b/Examples/Plugins/org.mitk.example.gui.imaging/src/internal/colourimageprocessing/QmitkColourImageProcessingView.cpp @@ -1,264 +1,270 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #include "QmitkColourImageProcessingView.h" #include "ui_QmitkColourImageProcessingViewControls.h" #include "mitkColourImageProcessor.h" #include "mitkDataNodeObject.h" #include "mitkTransferFunction.h" #include "mitkTransferFunctionProperty.h" #include "QmitkColorTransferFunctionCanvas.h" #include "QmitkPiecewiseFunctionCanvas.h" #include #include #include #include #include #include #include const std::string QmitkColourImageProcessingView::VIEW_ID = "org.mitk.views.colourimageprocessing"; QmitkColourImageProcessingView::QmitkColourImageProcessingView() : m_Controls(nullptr) { m_Color[0] = 255; m_Color[1] = 0; m_Color[2] = 0; } void QmitkColourImageProcessingView::SetFocus() { m_Controls->m_ConvertImageToRGBA->setFocus(); } void QmitkColourImageProcessingView::CreateQtPartControl(QWidget *parent) { if (!m_Controls) { m_Controls = new Ui::QmitkColourImageProcessingViewControls; m_Controls->setupUi(parent); connect(m_Controls->m_ConvertImageToRGBA, SIGNAL(clicked(bool)), this, SLOT(OnConvertToRGBAImage())); connect(m_Controls->m_ConvertImageMaskToRGBA, SIGNAL(clicked(bool)), this, SLOT(OnConvertToRGBAImage())); connect(m_Controls->m_ConvertImageMaskColorToRGBA, SIGNAL(clicked(bool)), this, SLOT(OnConvertImageMaskColorToRGBAImage())); connect(m_Controls->m_ColorButton, SIGNAL(clicked(bool)), this, SLOT(OnChangeColor())); connect(m_Controls->m_CombineRGBAButton, SIGNAL(clicked(bool)), this, SLOT(OnCombineRGBA())); m_Controls->m_ImageSelectedLabel->hide(); m_Controls->m_NoImageSelectedLabel->show(); } } void QmitkColourImageProcessingView::OnSelectionChanged(berry::IWorkbenchPart::Pointer /*part*/, const QList &nodes) { if (!nodes.isEmpty()) { QList selectedNodes; foreach (const mitk::DataNode::Pointer node, nodes) { if (node.IsNotNull()) { mitk::Image *image = dynamic_cast(node->GetData()); if(nullptr != image) { if (image->GetDimension() >= 3) { selectedNodes.push_back(node); } } } } mitk::DataNode::Pointer node; if (selectedNodes.size() > 0) { node = selectedNodes[0]; m_SelectedNode = node; m_Controls->m_NoImageSelectedLabel->hide(); m_Controls->m_ImageSelectedLabel->show(); std::string infoText = std::string("Selected Image: ") + node->GetName(); if (selectedNodes.size() > 1) { mitk::DataNode::Pointer node2; node2 = selectedNodes[1]; m_SelectedNode2 = node2; infoText = infoText + " and " + node2->GetName(); } else { m_SelectedNode2 = nullptr; } m_Controls->m_ImageSelectedLabel->setText(QString(infoText.c_str())); } else { m_Controls->m_ImageSelectedLabel->hide(); m_Controls->m_NoImageSelectedLabel->show(); m_SelectedNode = nullptr; m_SelectedNode2 = nullptr; } } } void QmitkColourImageProcessingView::OnConvertToRGBAImage() { - if (m_SelectedNode.IsExpired()) - return; - auto selectedNode = m_SelectedNode.Lock(); + if (selectedNode.IsNull()) + return; + mitk::TransferFunctionProperty::Pointer transferFunctionProp = dynamic_cast(selectedNode->GetProperty("TransferFunction")); if (transferFunctionProp.IsNull()) return; mitk::TransferFunction::Pointer tf = transferFunctionProp->GetValue(); if (tf.IsNull()) return; mitk::mitkColourImageProcessor CImageProcessor; mitk::Image::Pointer RGBAImageResult; + auto selectedNode2 = m_SelectedNode2.Lock(); - if (!m_SelectedNode2.IsExpired()) + if (selectedNode2.IsNotNull()) { RGBAImageResult = CImageProcessor.convertWithBinaryToRGBAImage(dynamic_cast(selectedNode->GetData()), - dynamic_cast(m_SelectedNode2.Lock()->GetData()), + dynamic_cast(selectedNode2->GetData()), tf); } else { RGBAImageResult = CImageProcessor.convertToRGBAImage(dynamic_cast(selectedNode->GetData()), tf); } if (!RGBAImageResult) { QMessageBox::warning(nullptr, "Warning", QString("Unsupported pixeltype")); return; } mitk::DataNode::Pointer dtn = mitk::DataNode::New(); dtn->SetData(RGBAImageResult); dtn->SetName(selectedNode->GetName() + "_RGBA"); this->GetDataStorage()->Add(dtn); // add as a child, because the segmentation "derives" from the original MITK_INFO << "convertToRGBAImage finish"; } void QmitkColourImageProcessingView::OnConvertImageMaskColorToRGBAImage() { - if (m_SelectedNode.IsExpired()) - return; - auto selectedNode = m_SelectedNode.Lock(); + if (selectedNode.IsNull()) + return; + mitk::TransferFunctionProperty::Pointer transferFunctionProp = dynamic_cast(selectedNode->GetProperty("TransferFunction")); if (transferFunctionProp.IsNull()) return; mitk::TransferFunction::Pointer tf = transferFunctionProp->GetValue(); if (tf.IsNull()) return; mitk::mitkColourImageProcessor CImageProcessor; mitk::Image::Pointer RGBAImageResult; + auto selectedNode2 = m_SelectedNode2.Lock(); - if (!m_SelectedNode2.IsExpired()) + if (selectedNode2.IsNotNull()) { RGBAImageResult = CImageProcessor.convertWithBinaryAndColorToRGBAImage(dynamic_cast(selectedNode->GetData()), - dynamic_cast(m_SelectedNode2.Lock()->GetData()), + dynamic_cast(selectedNode2->GetData()), tf, m_Color); } else { RGBAImageResult = CImageProcessor.convertToRGBAImage(dynamic_cast(selectedNode->GetData()), tf); } if (!RGBAImageResult) { QMessageBox::warning(nullptr, "Warning", QString("Unsupported pixeltype")); return; } mitk::DataNode::Pointer dtn = mitk::DataNode::New(); dtn->SetData(RGBAImageResult); dtn->SetName(selectedNode->GetName() + "_RGBA"); this->GetDataStorage()->Add(dtn); // add as a child, because the segmentation "derives" from the original } void QmitkColourImageProcessingView::OnChangeColor() { QColor color = QColorDialog::getColor(); if (color.spec() == 0) { color.setRed(255); color.setGreen(0); color.setBlue(0); } m_Color[0] = color.red(); m_Color[1] = color.green(); m_Color[2] = color.blue(); m_Controls->m_ColorButton->setStyleSheet( QString("background-color:rgb(%1,%2, %3)").arg(color.red()).arg(color.green()).arg(color.blue())); } void QmitkColourImageProcessingView::OnCombineRGBA() { - if (m_SelectedNode.IsExpired()) + auto selectedNode = m_SelectedNode.Lock(); + + if (selectedNode.IsNull()) return; - if (m_SelectedNode2.IsExpired()) + auto selectedNode2 = m_SelectedNode2.Lock(); + + if (selectedNode2.IsNull()) return; mitk::mitkColourImageProcessor CImageProcessor; mitk::Image::Pointer RGBAImageResult; - RGBAImageResult = CImageProcessor.combineRGBAImage(dynamic_cast(m_SelectedNode.Lock()->GetData()), - dynamic_cast(m_SelectedNode2.Lock()->GetData())); + RGBAImageResult = CImageProcessor.combineRGBAImage(dynamic_cast(selectedNode->GetData()), + dynamic_cast(selectedNode2->GetData())); MITK_INFO << "RGBAImage Result"; mitk::DataNode::Pointer dtn = mitk::DataNode::New(); dtn->SetData(RGBAImageResult); this->GetDataStorage()->Add(dtn); // add as a child, because the segmentation "derives" from the original } diff --git a/Examples/Plugins/org.mitk.example.gui.imaging/src/internal/volumetry/QmitkVolumetryView.cpp b/Examples/Plugins/org.mitk.example.gui.imaging/src/internal/volumetry/QmitkVolumetryView.cpp index 97acc0b0b0..eb2ce45ff9 100644 --- a/Examples/Plugins/org.mitk.example.gui.imaging/src/internal/volumetry/QmitkVolumetryView.cpp +++ b/Examples/Plugins/org.mitk.example.gui.imaging/src/internal/volumetry/QmitkVolumetryView.cpp @@ -1,246 +1,252 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #include "QmitkVolumetryView.h" #include "ui_QmitkVolumetryViewControls.h" #include "mitkImageStatisticsHolder.h" #include "mitkVolumeCalculator.h" #include #include #include #include const std::string QmitkVolumetryView::VIEW_ID = "org.mitk.views.volumetry"; QmitkVolumetryView::QmitkVolumetryView() : m_Controls(nullptr), m_ParentWidget(nullptr) { } void QmitkVolumetryView::CreateQtPartControl(QWidget *parent) { if (!m_Controls) { m_ParentWidget = parent; // create GUI widgets m_Controls = new Ui::QmitkVolumetryViewControls; m_Controls->setupUi(parent); this->CreateConnections(); } } void QmitkVolumetryView::SetFocus() { m_Controls->m_CalcButton->setFocus(); } void QmitkVolumetryView::CreateConnections() { if (m_Controls) { connect( (QObject *)(m_Controls->m_ThresholdSlider), SIGNAL(valueChanged(int)), this, SLOT(OnThresholdSliderChanged(int))); connect((QObject *)(m_Controls->m_CalcButton), SIGNAL(clicked()), this, SLOT(OnCalculateVolume())); connect((QObject *)(m_Controls->m_TimeSeriesButton), SIGNAL(clicked()), this, SLOT(OnTimeSeriesButtonClicked())); connect((QObject *)(m_Controls->m_SaveCsvButton), SIGNAL(clicked()), this, SLOT(OnSaveCsvButtonClicked())); } } void QmitkVolumetryView::OnSelectionChanged(berry::IWorkbenchPart::Pointer /*part*/, const QList &nodes) { m_SelectedDataNode = nullptr; if (!nodes.isEmpty() && dynamic_cast(nodes.front()->GetData())) { m_SelectedDataNode = nodes.front(); m_ParentWidget->setEnabled(true); } if (m_SelectedDataNode.IsExpired() || m_SelectedDataNode == m_OverlayNode) { m_SelectedDataNode = nullptr; m_ParentWidget->setEnabled(false); return; } if (m_OverlayNode) { this->GetDataStorage()->Remove(m_OverlayNode); m_OverlayNode = nullptr; } this->CreateOverlayChild(); m_Controls->m_CalcButton->setEnabled(false); m_Controls->m_TimeSeriesButton->setEnabled(false); m_Controls->m_SaveCsvButton->setEnabled(false); m_Controls->m_TextEdit->clear(); mitk::Image *image = dynamic_cast(m_SelectedDataNode.Lock()->GetData()); image->Update(); if (image && image->IsInitialized()) { if (image->GetDimension() == 4) { m_Controls->m_TimeSeriesButton->setEnabled(true); } else { m_Controls->m_CalcButton->setEnabled(true); } int minVal = (int)image->GetStatistics()->GetScalarValue2ndMin(); int maxVal = (int)image->GetStatistics()->GetScalarValueMaxNoRecompute(); if (minVal == maxVal) --minVal; m_Controls->m_ThresholdSlider->setMinimum(minVal); m_Controls->m_ThresholdSlider->setMaximum(maxVal); m_Controls->m_ThresholdSlider->setEnabled(true); this->UpdateSlider(); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } } void QmitkVolumetryView::OnCalculateVolume() { - if (!m_SelectedDataNode.IsExpired()) + auto selectedDataNode = m_SelectedDataNode.Lock(); + + if (selectedDataNode.IsNotNull()) { - mitk::Image *image = dynamic_cast(m_SelectedDataNode.Lock()->GetData()); + mitk::Image *image = dynamic_cast(selectedDataNode->GetData()); std::cout << "Dimension:" << image->GetDimension() << std::endl; std::cout << "Dimension[3]:" << image->GetDimension(3) << std::endl; mitk::VolumeCalculator::Pointer volCalc = mitk::VolumeCalculator::New(); volCalc->SetImage(image); volCalc->SetThreshold(m_Controls->m_ThresholdSlider->value()); volCalc->ComputeVolume(); std::stringstream vs; vs << volCalc->GetVolume() << " ml"; m_Controls->m_Result->setText(vs.str().c_str()); } } void QmitkVolumetryView::OnTimeSeriesButtonClicked() { - if (!m_SelectedDataNode.IsExpired()) + auto selectedDataNode = m_SelectedDataNode.Lock(); + + if (selectedDataNode.IsNotNull()) { - mitk::Image *image = dynamic_cast(m_SelectedDataNode.Lock()->GetData()); + mitk::Image *image = dynamic_cast(selectedDataNode->GetData()); mitk::VolumeCalculator::Pointer volCalc = mitk::VolumeCalculator::New(); volCalc->SetImage(image); volCalc->SetThreshold(m_Controls->m_ThresholdSlider->value()); volCalc->ComputeVolume(); std::vector volumes = volCalc->GetVolumes(); std::stringstream vs; int timeStep = 0; for (auto it = volumes.begin(); it != volumes.end(); it++) { vs << timeStep++ << "\t" << *it << std::endl; } m_Controls->m_TextEdit->setText(vs.str().c_str()); m_Controls->m_TextEdit->setTabStopWidth(20); m_Controls->m_SaveCsvButton->setEnabled(true); } } const mitk::DataNode *QmitkVolumetryView::GetImageNode() const { return m_SelectedDataNode.Lock(); } void QmitkVolumetryView::UpdateSlider() { - if (!m_SelectedDataNode.IsExpired() && dynamic_cast(m_SelectedDataNode.Lock()->GetData())) + auto selectedDataNode = m_SelectedDataNode.Lock(); + + if (selectedDataNode.IsNotNull() && dynamic_cast(selectedDataNode->GetData())) { int intSliderValue = (int)m_Controls->m_ThresholdSlider->value(); QString stringSliderValue; stringSliderValue.setNum(intSliderValue); m_Controls->m_ThresholdLineEdit->setText(stringSliderValue); } } void QmitkVolumetryView::UpdateSliderLabel() { int sliderValue = atoi(m_Controls->m_ThresholdLineEdit->text().toLatin1()); m_Controls->m_ThresholdSlider->setValue(sliderValue); this->UpdateSlider(); } void QmitkVolumetryView::OnThresholdSliderChanged(int value) { if (m_OverlayNode) { m_OverlayNode->SetLevelWindow(mitk::LevelWindow(value, 1)); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); this->UpdateSlider(); } } void QmitkVolumetryView::CreateOverlayChild() { - if (!m_SelectedDataNode.IsExpired()) - { - auto selectedDataNode = m_SelectedDataNode.Lock(); + auto selectedDataNode = m_SelectedDataNode.Lock(); + if (selectedDataNode.IsNotNull()) + { m_OverlayNode = mitk::DataNode::New(); mitk::StringProperty::Pointer nameProp = mitk::StringProperty::New("volume threshold overlay image"); m_OverlayNode->SetProperty("reslice interpolation", selectedDataNode->GetProperty("reslice interpolation")); m_OverlayNode->SetProperty("name", nameProp); m_OverlayNode->SetData(selectedDataNode->GetData()); m_OverlayNode->SetColor(0.0, 1.0, 0.0); m_OverlayNode->SetOpacity(.25); int layer = 0; selectedDataNode->GetIntProperty("layer", layer); m_OverlayNode->SetIntProperty("layer", layer + 1); m_OverlayNode->SetLevelWindow(mitk::LevelWindow(m_Controls->m_ThresholdSlider->value(), 1)); this->GetDataStorage()->Add(m_OverlayNode); } } mitk::DataNode *QmitkVolumetryView::GetOverlayNode() const { return m_OverlayNode; } mitk::Image *QmitkVolumetryView::GetImage() const { return dynamic_cast(m_SelectedDataNode.Lock()->GetData()); } void QmitkVolumetryView::OnSaveCsvButtonClicked() { static QString lastSavePath = QDir::homePath(); QString s = QFileDialog::getSaveFileName(this->m_ParentWidget, "Save as..", lastSavePath, "CSV Files (*.csv)"); /*"Save file dialog" "Choose a filename to save under" );*/ if (!s.isEmpty()) { lastSavePath = s; QFile saveFile(s); if (saveFile.open(QIODevice::WriteOnly)) { QTextStream stream(&saveFile); stream << m_Controls->m_TextEdit->toPlainText().replace('\t', ';'); saveFile.close(); } else { // QMessageBox::critical(nullptr,"Save Error!",QString("Saving of CSV failed! Couldn't open output file \"") + // saveFile + QString("\""),QMessageBox:Ok,QMessageBox::NoButton); // QMessageBox::critical(nullptr,"Save Error!","Saving of CSV failed! Couldn't open output file \"" + saveFile.name() // +"\"",QMessageBox::Ok,QMessageBox::NoButton); } } } diff --git a/Modules/AlgorithmsExt/src/mitkSegmentationSink.cpp b/Modules/AlgorithmsExt/src/mitkSegmentationSink.cpp index 48493b47ef..3da0078efc 100644 --- a/Modules/AlgorithmsExt/src/mitkSegmentationSink.cpp +++ b/Modules/AlgorithmsExt/src/mitkSegmentationSink.cpp @@ -1,91 +1,94 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #include "mitkSegmentationSink.h" #include "mitkDataStorage.h" #include "mitkRenderingManager.h" namespace mitk { SegmentationSink::SegmentationSink() {} SegmentationSink::~SegmentationSink() {} void SegmentationSink::Initialize(const NonBlockingAlgorithm *other) { Superclass::Initialize(other); // sinks should be called explicitly from the tool, because otherwise the order of setting "Input" and "Group node" // would matter UnDefineTriggerParameter("Input"); // some basedata output DataNode::Pointer groupNode; bool showResult(true); if (other) { other->GetPointerParameter("Group node", groupNode); other->GetParameter("Show result", showResult); } SetPointerParameter("Group node", groupNode); SetParameter("Show result", showResult); } bool SegmentationSink::ReadyToRun() { Image::Pointer image; GetPointerParameter("Input", image); DataNode::Pointer groupNode; GetPointerParameter("Group node", groupNode); return image.IsNotNull() && groupNode.IsNotNull(); } bool SegmentationSink::ThreadedUpdateFunction() { return true; } /// to be called by subclasses when they want to insert some resulting object (binary image, surface, ...) into the /// data tree void SegmentationSink::InsertBelowGroupNode(mitk::DataNode *node) { DataNode *groupNode = GetGroupNode(); - if (!m_DataStorage.IsExpired()) + auto dataStorage = m_DataStorage.Lock(); + + if (dataStorage.IsNotNull()) { if (node) node->GetData()->DisconnectPipeline(); - m_DataStorage.Lock()->Add(node, groupNode); + dataStorage->Add(node, groupNode); } RenderingManager::GetInstance()->RequestUpdateAll(); } DataNode *SegmentationSink::GetGroupNode() { DataNode::Pointer groupNode; GetPointerParameter("Group node", groupNode); return groupNode.GetPointer(); } DataNode *SegmentationSink::LookForPointerTargetBelowGroupNode(const char *name) { DataNode::Pointer groupNode; GetPointerParameter("Group node", groupNode); + auto dataStorage = m_DataStorage.Lock(); - if (groupNode.IsNotNull() && !m_DataStorage.IsExpired()) + if (groupNode.IsNotNull() && dataStorage.IsNotNull()) { - return m_DataStorage.Lock()->GetNamedDerivedNode(name, groupNode, true); + return dataStorage->GetNamedDerivedNode(name, groupNode, true); } return nullptr; } } // namespace diff --git a/Modules/Core/src/DataManagement/mitkNodePredicateFirstLevel.cpp b/Modules/Core/src/DataManagement/mitkNodePredicateFirstLevel.cpp index 6f0fc9037d..996bfac5b5 100644 --- a/Modules/Core/src/DataManagement/mitkNodePredicateFirstLevel.cpp +++ b/Modules/Core/src/DataManagement/mitkNodePredicateFirstLevel.cpp @@ -1,33 +1,35 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #include "mitkNodePredicateFirstLevel.h" mitk::NodePredicateFirstLevel::NodePredicateFirstLevel(mitk::DataStorage *ds) : NodePredicateBase(), m_DataStorage(ds) { } mitk::NodePredicateFirstLevel::~NodePredicateFirstLevel() { } bool mitk::NodePredicateFirstLevel::CheckNode(const mitk::DataNode *node) const { if (node == nullptr) throw std::invalid_argument("NodePredicateFirstLevel: invalid node"); - if (m_DataStorage.IsExpired()) + auto dataStorage = m_DataStorage.Lock(); + + if (dataStorage.IsNull()) throw std::invalid_argument("NodePredicateFirstLevel: DataStorage is invalid"); - mitk::DataStorage::SetOfObjects::ConstPointer list = m_DataStorage.Lock()->GetSources(node, nullptr, true); + mitk::DataStorage::SetOfObjects::ConstPointer list = dataStorage->GetSources(node, nullptr, true); return (list->Size() == 0); } diff --git a/Modules/Core/src/Interactions/mitkDataInteractor.cpp b/Modules/Core/src/Interactions/mitkDataInteractor.cpp index e80023d1db..ab72645a42 100644 --- a/Modules/Core/src/Interactions/mitkDataInteractor.cpp +++ b/Modules/Core/src/Interactions/mitkDataInteractor.cpp @@ -1,105 +1,111 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #include "mitkDataInteractor.h" #include "mitkDataNode.h" #include "mitkStateMachineState.h" namespace mitk { itkEventMacroDefinition(DataInteractorEvent, itk::AnyEvent); itkEventMacroDefinition(StartInteraction, DataInteractorEvent); itkEventMacroDefinition(ResultReady, DataInteractorEvent); } // Predefined internal events/signals const std::string mitk::DataInteractor::IntDeactivateMe = "DeactivateMe"; const std::string mitk::DataInteractor::IntLeaveWidget = "LeaveWidget"; const std::string mitk::DataInteractor::IntEnterWidget = "EnterWidget"; mitk::DataInteractor::DataInteractor() { } mitk::DataInteractor::~DataInteractor() { - if (!m_DataNode.IsExpired()) - { - auto dataNode = m_DataNode.Lock(); + auto dataNode = m_DataNode.Lock(); + if (dataNode.IsNotNull()) + { if (dataNode->GetDataInteractor() == this) dataNode->SetDataInteractor(nullptr); } } mitk::DataNode *mitk::DataInteractor::GetDataNode() const { return m_DataNode.Lock(); } void mitk::DataInteractor::SetDataNode(DataNode *dataNode) { if (dataNode == m_DataNode) return; - if (!m_DataNode.IsExpired()) - m_DataNode.Lock()->SetDataInteractor(nullptr); + auto lockedDataNode = m_DataNode.Lock(); + + if (lockedDataNode.IsNotNull()) + lockedDataNode->SetDataInteractor(nullptr); m_DataNode = dataNode; - if (dataNode != nullptr) - m_DataNode.Lock()->SetDataInteractor(this); + lockedDataNode = m_DataNode.Lock(); + + if (lockedDataNode.IsNotNull()) + lockedDataNode->SetDataInteractor(this); this->DataNodeChanged(); } int mitk::DataInteractor::GetLayer() const { int layer = -1; - if (!m_DataNode.IsExpired()) - m_DataNode.Lock()->GetIntProperty("layer", layer); + auto dataNode = m_DataNode.Lock(); + + if (dataNode.IsNotNull()) + dataNode->GetIntProperty("layer", layer); return layer; } void mitk::DataInteractor::ConnectActionsAndFunctions() { MITK_WARN << "DataInteractor::ConnectActionsAndFunctions() is not implemented."; } mitk::ProcessEventMode mitk::DataInteractor::GetMode() const { auto mode = this->GetCurrentState()->GetMode(); if (mode == "PREFER_INPUT") return PREFERINPUT; if (mode == "GRAB_INPUT") return GRABINPUT; return REGULAR; } void mitk::DataInteractor::NotifyStart() { this->GetDataNode()->InvokeEvent(StartInteraction()); } void mitk::DataInteractor::NotifyResultReady() { this->GetDataNode()->InvokeEvent(ResultReady()); } void mitk::DataInteractor::DataNodeChanged() { } diff --git a/Modules/Core/src/Interactions/mitkDispatcher.cpp b/Modules/Core/src/Interactions/mitkDispatcher.cpp index 246bbbcf57..8072e61860 100644 --- a/Modules/Core/src/Interactions/mitkDispatcher.cpp +++ b/Modules/Core/src/Interactions/mitkDispatcher.cpp @@ -1,280 +1,289 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #include "mitkDispatcher.h" #include "mitkInteractionEvent.h" #include "mitkInteractionEventObserver.h" #include "mitkInternalEvent.h" #include "usGetModuleContext.h" namespace { struct cmp { bool operator()(mitk::WeakPointer d1, mitk::WeakPointer d2) { return (d1.Lock()->GetLayer() > d2.Lock()->GetLayer()); } }; } mitk::Dispatcher::Dispatcher(const std::string &rendererName) : m_ProcessingMode(REGULAR) { // LDAP filter string to find all listeners specific for the renderer // corresponding to this dispatcher std::string specificRenderer = "(rendererName=" + rendererName + ")"; // LDAP filter string to find all listeners that are not specific // to any renderer std::string anyRenderer = "(!(rendererName=*))"; // LDAP filter string to find only instances of InteractionEventObserver std::string classInteractionEventObserver = "(" + us::ServiceConstants::OBJECTCLASS() + "=" + us_service_interface_iid() + ")"; // Configure the LDAP filter to find all instances of InteractionEventObserver // that are specific to this dispatcher or unspecific to any dispatchers (real global listener) us::LDAPFilter filter("(&(|" + specificRenderer + anyRenderer + ")" + classInteractionEventObserver + ")"); // Give the filter to the ObserverTracker m_EventObserverTracker = new us::ServiceTracker(us::GetModuleContext(), filter); m_EventObserverTracker->Open(); } void mitk::Dispatcher::AddDataInteractor(const DataNode *dataNode) { RemoveDataInteractor(dataNode); RemoveOrphanedInteractors(); auto dataInteractor = dataNode->GetDataInteractor().GetPointer(); if (dataInteractor != nullptr) m_Interactors.push_back(dataInteractor); } /* * Note: One DataInteractor can only have one DataNode and vice versa, * BUT the m_Interactors list may contain another DataInteractor that is still connected to this DataNode, * in this case we have to remove >1 DataInteractor. (Some special case of switching DataNodes between DataInteractors * and registering a * DataNode to a DataStorage after assigning it to an DataInteractor) */ void mitk::Dispatcher::RemoveDataInteractor(const DataNode *dataNode) { for (auto it = m_Interactors.begin(); it != m_Interactors.end();) { - if ((*it).IsExpired() || (*it).Lock()->GetDataNode() == nullptr || (*it).Lock()->GetDataNode() == dataNode) + auto interactor = it->Lock(); + if (interactor.IsNull() || interactor->GetDataNode() == nullptr || interactor->GetDataNode() == dataNode) { it = m_Interactors.erase(it); } else { ++it; } } } size_t mitk::Dispatcher::GetNumberOfInteractors() { return m_Interactors.size(); } mitk::Dispatcher::~Dispatcher() { m_EventObserverTracker->Close(); delete m_EventObserverTracker; m_Interactors.clear(); } bool mitk::Dispatcher::ProcessEvent(InteractionEvent *event) { InteractionEvent::Pointer p = event; bool eventIsHandled = false; /* Filter out and handle Internal Events separately */ auto *internalEvent = dynamic_cast(event); if (internalEvent != nullptr) { eventIsHandled = HandleInternalEvent(internalEvent); // InternalEvents that are handled are not sent to the listeners if (eventIsHandled) { return true; } } + + auto selectedInteractor = m_SelectedInteractor.Lock(); + switch (m_ProcessingMode) { case CONNECTEDMOUSEACTION: // finished connected mouse action if (std::strcmp(p->GetNameOfClass(), "MouseReleaseEvent") == 0) { m_ProcessingMode = REGULAR; - if (!m_SelectedInteractor.IsExpired()) - eventIsHandled = m_SelectedInteractor.Lock()->HandleEvent(event, m_SelectedInteractor.Lock()->GetDataNode()); + if (selectedInteractor.IsNotNull()) + eventIsHandled = selectedInteractor->HandleEvent(event, selectedInteractor->GetDataNode()); m_SelectedInteractor = nullptr; } // give event to selected interactor - if (eventIsHandled == false && !m_SelectedInteractor.IsExpired()) - eventIsHandled = m_SelectedInteractor.Lock()->HandleEvent(event, m_SelectedInteractor.Lock()->GetDataNode()); + selectedInteractor = m_SelectedInteractor.Lock(); + + if (eventIsHandled == false && selectedInteractor.IsNotNull()) + eventIsHandled = selectedInteractor->HandleEvent(event, selectedInteractor->GetDataNode()); break; case GRABINPUT: - if (!m_SelectedInteractor.IsExpired()) + if (selectedInteractor.IsNotNull()) { - eventIsHandled = m_SelectedInteractor.Lock()->HandleEvent(event, m_SelectedInteractor.Lock()->GetDataNode()); - SetEventProcessingMode(m_SelectedInteractor.Lock()); + eventIsHandled = selectedInteractor->HandleEvent(event,selectedInteractor->GetDataNode()); + SetEventProcessingMode(selectedInteractor); } break; case PREFERINPUT: - if (!m_SelectedInteractor.IsExpired() && - m_SelectedInteractor.Lock()->HandleEvent(event, m_SelectedInteractor.Lock()->GetDataNode()) == true) + if (selectedInteractor.IsNotNull() && + selectedInteractor->HandleEvent(event, selectedInteractor->GetDataNode()) == true) { - SetEventProcessingMode(m_SelectedInteractor.Lock()); + SetEventProcessingMode(selectedInteractor); eventIsHandled = true; } break; case REGULAR: break; } // Standard behavior. Is executed in STANDARD mode and PREFERINPUT mode, if preferred interactor rejects event. if (m_ProcessingMode == REGULAR || (m_ProcessingMode == PREFERINPUT && eventIsHandled == false)) { if (std::strcmp(p->GetNameOfClass(), "MousePressEvent") == 0) RenderingManager::GetInstance()->SetRenderWindowFocus(event->GetSender()->GetRenderWindow()); m_Interactors.sort(cmp()); // sorts interactors by layer (descending); // copy the list to prevent iterator invalidation as executing actions // in HandleEvent() can cause the m_Interactors list to be updated const ListInteractorType tmpInteractorList(m_Interactors); ListInteractorType::const_iterator it; for (it = tmpInteractorList.cbegin(); it != tmpInteractorList.cend(); ++it) { - if (!(*it).IsExpired() && (*it).Lock()->HandleEvent(event, (*it).Lock()->GetDataNode())) + auto interactor = it->Lock(); + if (interactor.IsNotNull() && interactor->HandleEvent(event, interactor->GetDataNode())) { // Interactor can be deleted during HandleEvent(), so check it again - if (!(*it).IsExpired()) + interactor = it->Lock(); + if (interactor.IsNotNull()) { // if an event is handled several properties are checked, in order to determine the processing mode of the // dispatcher - SetEventProcessingMode((*it).Lock()); + SetEventProcessingMode(interactor); } if (std::strcmp(p->GetNameOfClass(), "MousePressEvent") == 0 && m_ProcessingMode == REGULAR) { m_SelectedInteractor = *it; m_ProcessingMode = CONNECTEDMOUSEACTION; } eventIsHandled = true; break; } } } /* Notify InteractionEventObserver */ const std::vector> listEventObserver = m_EventObserverTracker->GetServiceReferences(); for (auto it = listEventObserver.cbegin(); it != listEventObserver.cend(); ++it) { InteractionEventObserver *interactionEventObserver = m_EventObserverTracker->GetService(*it); if (interactionEventObserver != nullptr) { if (interactionEventObserver->IsEnabled()) { interactionEventObserver->Notify(event, eventIsHandled); } } } // Process event queue if (!m_QueuedEvents.empty()) { InteractionEvent::Pointer e = m_QueuedEvents.front(); m_QueuedEvents.pop_front(); ProcessEvent(e); } return eventIsHandled; } /* * Checks if DataNodes associated with DataInteractors point back to them. * If not remove the DataInteractors. (This can happen when s.o. tries to set DataNodes to multiple DataInteractors) */ void mitk::Dispatcher::RemoveOrphanedInteractors() { for (auto it = m_Interactors.begin(); it != m_Interactors.end();) { - if ((*it).IsExpired()) + auto interactor = it->Lock(); + if (interactor.IsNull()) { it = m_Interactors.erase(it); } else { - DataNode::Pointer node = (*it).Lock()->GetDataNode(); + DataNode::Pointer node = interactor->GetDataNode(); if (node.IsNull()) { it = m_Interactors.erase(it); } else { - DataInteractor::Pointer interactor = node->GetDataInteractor(); + interactor = node->GetDataInteractor(); if (interactor != it->Lock().GetPointer()) { it = m_Interactors.erase(it); } else { ++it; } } } } } void mitk::Dispatcher::QueueEvent(InteractionEvent *event) { m_QueuedEvents.push_back(event); } void mitk::Dispatcher::SetEventProcessingMode(DataInteractor *dataInteractor) { m_ProcessingMode = dataInteractor->GetMode(); if (dataInteractor->GetMode() != REGULAR) { m_SelectedInteractor = dataInteractor; } } bool mitk::Dispatcher::HandleInternalEvent(InternalEvent *internalEvent) { if (internalEvent->GetSignalName() == DataInteractor::IntDeactivateMe && internalEvent->GetTargetInteractor() != nullptr) { internalEvent->GetTargetInteractor()->GetDataNode()->SetDataInteractor(nullptr); internalEvent->GetTargetInteractor()->SetDataNode(nullptr); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); return true; } return false; } diff --git a/Modules/Core/src/Interactions/mitkDisplayActionEventHandler.cpp b/Modules/Core/src/Interactions/mitkDisplayActionEventHandler.cpp index c4c14dc242..fb8d38945f 100644 --- a/Modules/Core/src/Interactions/mitkDisplayActionEventHandler.cpp +++ b/Modules/Core/src/Interactions/mitkDisplayActionEventHandler.cpp @@ -1,105 +1,108 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #include "mitkDisplayActionEventHandler.h" mitk::DisplayActionEventHandler::~DisplayActionEventHandler() { - if (!m_ObservableBroadcast.IsExpired()) - { - auto observableBroadcastPtr = m_ObservableBroadcast.Lock(); + auto observableBroadcastPtr = m_ObservableBroadcast.Lock(); + if (observableBroadcastPtr.IsNotNull()) + { // remove current observer for (const auto& tag : m_ObserverTags) { observableBroadcastPtr->RemoveObserver(tag); } m_ObserverTags.clear(); } } void mitk::DisplayActionEventHandler::SetObservableBroadcast(DisplayActionEventBroadcast* observableBroadcast) { if (m_ObservableBroadcast == observableBroadcast) { // no need to update the broadcast class return; } - if (!m_ObservableBroadcast.IsExpired()) - { - auto observableBroadcastPtr = m_ObservableBroadcast.Lock(); + auto observableBroadcastPtr = m_ObservableBroadcast.Lock(); + if (observableBroadcastPtr.IsNotNull()) + { // remove current observer for (const auto& tag : m_ObserverTags) { observableBroadcastPtr->RemoveObserver(tag); } m_ObserverTags.clear(); } // set new broadcast class m_ObservableBroadcast = observableBroadcast; } mitk::DisplayActionEventHandler::OberserverTagType mitk::DisplayActionEventHandler::ConnectDisplayActionEvent(const DisplayActionEvent& displayActionEvent, const StdFunctionCommand::ActionFunction& actionFunction, const StdFunctionCommand::FilterFunction& filterFunction) { - if (m_ObservableBroadcast.IsExpired()) + auto observableBroadcast = m_ObservableBroadcast.Lock(); + + if (observableBroadcast.IsNull()) { mitkThrow() << "No display action event broadcast class set to observe. Use 'SetObservableBroadcast' before connecting events."; } - auto observableBroadcast = m_ObservableBroadcast.Lock(); auto command = StdFunctionCommand::New(); command->SetCommandAction(actionFunction); command->SetCommandFilter(filterFunction); OberserverTagType tag = observableBroadcast->AddObserver(displayActionEvent, command); m_ObserverTags.push_back(tag); return tag; } void mitk::DisplayActionEventHandler::DisconnectObserver(OberserverTagType observerTag) { - if (m_ObservableBroadcast.IsExpired()) + auto observableBroadcast = m_ObservableBroadcast.Lock(); + + if (observableBroadcast.IsNull()) { mitkThrow() << "No display action event broadcast class set to observe. Use 'SetObservableBroadcast' before disconnecting observer."; } - auto observableBroadcast = m_ObservableBroadcast.Lock(); std::vector::iterator observerTagPosition = std::find(m_ObserverTags.begin(), m_ObserverTags.end(), observerTag); if (observerTagPosition != m_ObserverTags.end()) { observableBroadcast->RemoveObserver(observerTag); m_ObserverTags.erase(observerTagPosition); } } void mitk::DisplayActionEventHandler::InitActions() { - if (m_ObservableBroadcast.IsExpired()) + auto observableBroadcast = m_ObservableBroadcast.Lock(); + + if (observableBroadcast.IsNull()) { mitkThrow() << "No display action event broadcast class set to observe. Use 'SetObservableBroadcast' before initializing actions."; } - auto observableBroadcast = m_ObservableBroadcast.Lock(); // remove all current display action events as observer auto allObserverTags = GetAllObserverTags(); for (const auto& tag : allObserverTags) { observableBroadcast->RemoveObserver(tag); } m_ObserverTags.clear(); InitActionsImpl(); } diff --git a/Modules/Core/src/Rendering/mitkPlaneGeometryDataVtkMapper3D.cpp b/Modules/Core/src/Rendering/mitkPlaneGeometryDataVtkMapper3D.cpp index c49b3d3cc5..3a4b4ff08d 100644 --- a/Modules/Core/src/Rendering/mitkPlaneGeometryDataVtkMapper3D.cpp +++ b/Modules/Core/src/Rendering/mitkPlaneGeometryDataVtkMapper3D.cpp @@ -1,584 +1,585 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #include "mitkPlaneGeometryDataVtkMapper3D.h" #include "mitkImageVtkMapper2D.h" #include "mitkNodePredicateDataType.h" #include "mitkNodePredicateOr.h" #include "mitkSmartPointerProperty.h" #include "mitkSurface.h" #include "mitkVtkRepresentationProperty.h" #include "mitkWeakPointerProperty.h" #include "vtkMitkLevelWindowFilter.h" #include "vtkNeverTranslucentTexture.h" #include #include #include #include #include #include #include #include #include #include #include #include namespace mitk { PlaneGeometryDataVtkMapper3D::PlaneGeometryDataVtkMapper3D() : m_NormalsActorAdded(false), m_DataStorage(nullptr) { m_EdgeTuber = vtkTubeFilter::New(); m_EdgeMapper = vtkPolyDataMapper::New(); m_SurfaceCreator = PlaneGeometryDataToSurfaceFilter::New(); m_SurfaceCreatorBoundingBox = BoundingBox::New(); m_SurfaceCreatorPointsContainer = BoundingBox::PointsContainer::New(); m_Edges = vtkFeatureEdges::New(); m_Edges->BoundaryEdgesOn(); m_Edges->FeatureEdgesOff(); m_Edges->NonManifoldEdgesOff(); m_Edges->ManifoldEdgesOff(); m_EdgeTransformer = vtkTransformPolyDataFilter::New(); m_NormalsTransformer = vtkTransformPolyDataFilter::New(); m_EdgeActor = vtkActor::New(); m_BackgroundMapper = vtkPolyDataMapper::New(); m_BackgroundActor = vtkActor::New(); m_Prop3DAssembly = vtkAssembly::New(); m_ImageAssembly = vtkAssembly::New(); m_SurfaceCreatorBoundingBox->SetPoints(m_SurfaceCreatorPointsContainer); m_Cleaner = vtkCleanPolyData::New(); m_Cleaner->PieceInvariantOn(); m_Cleaner->ConvertLinesToPointsOn(); m_Cleaner->ConvertPolysToLinesOn(); m_Cleaner->ConvertStripsToPolysOn(); m_Cleaner->PointMergingOn(); // Make sure that the FeatureEdge algorithm is initialized with a "valid" // (though empty) input vtkPolyData *emptyPolyData = vtkPolyData::New(); m_Cleaner->SetInputData(emptyPolyData); emptyPolyData->Delete(); m_Edges->SetInputConnection(m_Cleaner->GetOutputPort()); m_EdgeTransformer->SetInputConnection(m_Edges->GetOutputPort()); m_EdgeTuber->SetInputConnection(m_EdgeTransformer->GetOutputPort()); m_EdgeTuber->SetVaryRadiusToVaryRadiusOff(); m_EdgeTuber->SetNumberOfSides(12); m_EdgeTuber->CappingOn(); m_EdgeMapper->SetInputConnection(m_EdgeTuber->GetOutputPort()); m_EdgeMapper->ScalarVisibilityOff(); m_BackgroundMapper->SetInputData(emptyPolyData); m_BackgroundMapper->Update(); m_EdgeActor->SetMapper(m_EdgeMapper); m_BackgroundActor->GetProperty()->SetAmbient(0.5); m_BackgroundActor->GetProperty()->SetColor(0.0, 0.0, 0.0); m_BackgroundActor->GetProperty()->SetOpacity(0.0); m_BackgroundActor->SetMapper(m_BackgroundMapper); vtkProperty *backfaceProperty = m_BackgroundActor->MakeProperty(); backfaceProperty->SetColor(0.0, 0.0, 0.0); m_BackgroundActor->SetBackfaceProperty(backfaceProperty); backfaceProperty->Delete(); m_FrontHedgeHog = vtkHedgeHog::New(); m_BackHedgeHog = vtkHedgeHog::New(); m_FrontNormalsMapper = vtkPolyDataMapper::New(); m_FrontNormalsMapper->SetInputConnection(m_FrontHedgeHog->GetOutputPort()); m_BackNormalsMapper = vtkPolyDataMapper::New(); m_Prop3DAssembly->AddPart(m_EdgeActor); m_Prop3DAssembly->AddPart(m_ImageAssembly); m_FrontNormalsActor = vtkActor::New(); m_FrontNormalsActor->SetMapper(m_FrontNormalsMapper); m_BackNormalsActor = vtkActor::New(); m_BackNormalsActor->SetMapper(m_BackNormalsMapper); m_ImageMapperDeletedCommand = MemberCommandType::New(); m_ImageMapperDeletedCommand->SetCallbackFunction(this, &PlaneGeometryDataVtkMapper3D::ImageMapperDeletedCallback); } PlaneGeometryDataVtkMapper3D::~PlaneGeometryDataVtkMapper3D() { m_ImageAssembly->Delete(); m_Prop3DAssembly->Delete(); m_EdgeTuber->Delete(); m_EdgeMapper->Delete(); m_EdgeTransformer->Delete(); m_Cleaner->Delete(); m_Edges->Delete(); m_NormalsTransformer->Delete(); m_EdgeActor->Delete(); m_BackgroundMapper->Delete(); m_BackgroundActor->Delete(); m_FrontNormalsMapper->Delete(); m_FrontNormalsActor->Delete(); m_FrontHedgeHog->Delete(); m_BackNormalsMapper->Delete(); m_BackNormalsActor->Delete(); m_BackHedgeHog->Delete(); for (auto it = m_ImageActors.begin(); it != m_ImageActors.end(); ++it) it->second.m_Actor->ReleaseGraphicsResources(nullptr); // Delete entries in m_ImageActors list one by one m_ImageActors.clear(); m_DataStorage = nullptr; } vtkProp *PlaneGeometryDataVtkMapper3D::GetVtkProp(mitk::BaseRenderer * /*renderer*/) { if ((this->GetDataNode() != nullptr) && (m_ImageAssembly != nullptr)) { // Do not transform the entire Prop3D assembly, but only the image part // here. The colored frame is transformed elsewhere (via m_EdgeTransformer), // since only vertices should be transformed there, not the poly data // itself, to avoid distortion for anisotropic datasets. m_ImageAssembly->SetUserTransform(this->GetDataNode()->GetVtkTransform()); } return m_Prop3DAssembly; } void PlaneGeometryDataVtkMapper3D::UpdateVtkTransform(mitk::BaseRenderer * /*renderer*/) { m_ImageAssembly->SetUserTransform(this->GetDataNode()->GetVtkTransform(this->GetTimestep())); } const PlaneGeometryData *PlaneGeometryDataVtkMapper3D::GetInput() { return static_cast(GetDataNode()->GetData()); } void PlaneGeometryDataVtkMapper3D::SetDataStorageForTexture(mitk::DataStorage *storage) { if (storage != nullptr && m_DataStorage != storage) { m_DataStorage = storage; this->Modified(); } } void PlaneGeometryDataVtkMapper3D::ImageMapperDeletedCallback(itk::Object *caller, const itk::EventObject & /*event*/) { auto *imageMapper = dynamic_cast(caller); if ((imageMapper != nullptr)) { if (m_ImageActors.count(imageMapper) > 0) { m_ImageActors[imageMapper].m_Sender = nullptr; // sender is already destroying itself m_ImageActors.erase(imageMapper); } } } void PlaneGeometryDataVtkMapper3D::GenerateDataForRenderer(BaseRenderer *renderer) { // Remove all actors from the assembly, and re-initialize it with the // edge actor m_ImageAssembly->GetParts()->RemoveAllItems(); bool visible = true; GetDataNode()->GetVisibility(visible, renderer, "visible"); if (!visible) { // visibility has explicitly to be set in the single actors // due to problems when using cell picking: // even if the assembly is invisible, the renderer contains // references to the assemblies parts. During picking the // visibility of each part is checked, and not only for the // whole assembly. m_ImageAssembly->VisibilityOff(); m_EdgeActor->VisibilityOff(); return; } // visibility has explicitly to be set in the single actors // due to problems when using cell picking: // even if the assembly is invisible, the renderer contains // references to the assemblies parts. During picking the // visibility of each part is checked, and not only for the // whole assembly. m_ImageAssembly->VisibilityOn(); bool drawEdges = true; this->GetDataNode()->GetBoolProperty("draw edges", drawEdges, renderer); m_EdgeActor->SetVisibility(drawEdges); PlaneGeometryData::ConstPointer input = this->GetInput(); if (input.IsNotNull() && (input->GetPlaneGeometry() != nullptr)) { SmartPointerProperty::Pointer surfacecreatorprop; surfacecreatorprop = dynamic_cast(GetDataNode()->GetProperty("surfacegeometry", renderer)); if ((surfacecreatorprop.IsNull()) || (surfacecreatorprop->GetSmartPointer().IsNull()) || ((m_SurfaceCreator = dynamic_cast(surfacecreatorprop->GetSmartPointer().GetPointer())) .IsNull())) { m_SurfaceCreator->PlaceByGeometryOn(); surfacecreatorprop = SmartPointerProperty::New(m_SurfaceCreator); GetDataNode()->SetProperty("surfacegeometry", surfacecreatorprop); } m_SurfaceCreator->SetInput(input); int res; if (GetDataNode()->GetIntProperty("xresolution", res, renderer)) { m_SurfaceCreator->SetXResolution(res); } if (GetDataNode()->GetIntProperty("yresolution", res, renderer)) { m_SurfaceCreator->SetYResolution(res); } double tubeRadius = 1.0; // Radius of tubular edge surrounding plane + auto dataStorage = m_DataStorage.Lock(); // Clip the PlaneGeometry with the reference geometry bounds (if available) if (input->GetPlaneGeometry()->HasReferenceGeometry()) { const BaseGeometry *referenceGeometry = input->GetPlaneGeometry()->GetReferenceGeometry(); BoundingBox::PointType boundingBoxMin, boundingBoxMax; boundingBoxMin = referenceGeometry->GetBoundingBox()->GetMinimum(); boundingBoxMax = referenceGeometry->GetBoundingBox()->GetMaximum(); if (referenceGeometry->GetImageGeometry()) { for (unsigned int i = 0; i < 3; ++i) { boundingBoxMin[i] -= 0.5; boundingBoxMax[i] -= 0.5; } } m_SurfaceCreatorPointsContainer->CreateElementAt(0) = boundingBoxMin; m_SurfaceCreatorPointsContainer->CreateElementAt(1) = boundingBoxMax; m_SurfaceCreatorBoundingBox->ComputeBoundingBox(); m_SurfaceCreator->SetBoundingBox(m_SurfaceCreatorBoundingBox); tubeRadius = referenceGeometry->GetDiagonalLength() / 450.0; } // If no reference geometry is available, clip with the current global // bounds - else if (!m_DataStorage.IsExpired()) + else if (dataStorage.IsNotNull()) { - m_SurfaceCreator->SetBoundingBox(m_DataStorage.Lock()->ComputeVisibleBoundingBox(nullptr, "includeInBoundingBox")); + m_SurfaceCreator->SetBoundingBox(dataStorage->ComputeVisibleBoundingBox(nullptr, "includeInBoundingBox")); tubeRadius = sqrt(m_SurfaceCreator->GetBoundingBox()->GetDiagonalLength2()) / 450.0; } // Calculate the surface of the PlaneGeometry m_SurfaceCreator->Update(); Surface *surface = m_SurfaceCreator->GetOutput(); // Check if there's something to display, otherwise return if ((surface->GetVtkPolyData() == nullptr) || (surface->GetVtkPolyData()->GetNumberOfCells() == 0)) { m_ImageAssembly->VisibilityOff(); return; } // add a graphical representation of the surface normals if requested DataNode *node = this->GetDataNode(); bool displayNormals = false; bool colorTwoSides = false; bool invertNormals = false; node->GetBoolProperty("draw normals 3D", displayNormals, renderer); node->GetBoolProperty("color two sides", colorTwoSides, renderer); node->GetBoolProperty("invert normals", invertNormals, renderer); // if we want to draw the display normals or render two sides we have to get the colors if (displayNormals || colorTwoSides) { // get colors float frontColor[3] = {0.0, 0.0, 1.0}; node->GetColor(frontColor, renderer, "front color"); float backColor[3] = {1.0, 0.0, 0.0}; node->GetColor(backColor, renderer, "back color"); if (displayNormals) { m_NormalsTransformer->SetInputData(surface->GetVtkPolyData()); m_NormalsTransformer->SetTransform(node->GetVtkTransform(this->GetTimestep())); m_FrontHedgeHog->SetInputConnection(m_NormalsTransformer->GetOutputPort()); m_FrontHedgeHog->SetVectorModeToUseNormal(); m_FrontHedgeHog->SetScaleFactor(invertNormals ? 1.0 : -1.0); m_FrontHedgeHog->Update(); m_FrontNormalsActor->GetProperty()->SetColor(frontColor[0], frontColor[1], frontColor[2]); m_BackHedgeHog->SetInputConnection(m_NormalsTransformer->GetOutputPort()); m_BackHedgeHog->SetVectorModeToUseNormal(); m_BackHedgeHog->SetScaleFactor(invertNormals ? -1.0 : 1.0); m_BackHedgeHog->Update(); m_BackNormalsActor->GetProperty()->SetColor(backColor[0], backColor[1], backColor[2]); // if there is no actor added yet, add one if (!m_NormalsActorAdded) { m_Prop3DAssembly->AddPart(m_FrontNormalsActor); m_Prop3DAssembly->AddPart(m_BackNormalsActor); m_NormalsActorAdded = true; } } // if we don't want to display normals AND there is an actor added remove the actor else if (m_NormalsActorAdded) { m_Prop3DAssembly->RemovePart(m_FrontNormalsActor); m_Prop3DAssembly->RemovePart(m_BackNormalsActor); m_NormalsActorAdded = false; } if (colorTwoSides) { if (!invertNormals) { m_BackgroundActor->GetProperty()->SetColor(backColor[0], backColor[1], backColor[2]); m_BackgroundActor->GetBackfaceProperty()->SetColor(frontColor[0], frontColor[1], frontColor[2]); } else { m_BackgroundActor->GetProperty()->SetColor(frontColor[0], frontColor[1], frontColor[2]); m_BackgroundActor->GetBackfaceProperty()->SetColor(backColor[0], backColor[1], backColor[2]); } } } // Add black background for all images (which may be transparent) m_BackgroundMapper->SetInputData(surface->GetVtkPolyData()); // m_ImageAssembly->AddPart(m_BackgroundActor); LayerSortedActorList layerSortedActors; // Traverse the data tree to find nodes resliced by ImageMapperGL2D // use a predicate to get all data nodes which are "images" or inherit from mitk::Image mitk::TNodePredicateDataType::Pointer predicateAllImages = mitk::TNodePredicateDataType::New(); mitk::DataStorage::SetOfObjects::ConstPointer all = m_DataStorage.Lock()->GetSubset(predicateAllImages); // process all found images for (mitk::DataStorage::SetOfObjects::ConstIterator it = all->Begin(); it != all->End(); ++it) { DataNode *node = it->Value(); if (node != nullptr) this->ProcessNode(node, renderer, surface, layerSortedActors); } // Add all image actors to the assembly, sorted according to // layer property LayerSortedActorList::iterator actorIt; for (actorIt = layerSortedActors.begin(); actorIt != layerSortedActors.end(); ++actorIt) { m_ImageAssembly->AddPart(actorIt->second); } // Configurate the tube-shaped frame: size according to the surface // bounds, color as specified in the plane's properties vtkPolyData *surfacePolyData = surface->GetVtkPolyData(); m_Cleaner->SetInputData(surfacePolyData); m_EdgeTransformer->SetTransform(this->GetDataNode()->GetVtkTransform(this->GetTimestep())); // Adjust the radius according to extent m_EdgeTuber->SetRadius(tubeRadius); // Get the plane's color and set the tube properties accordingly ColorProperty::Pointer colorProperty; colorProperty = dynamic_cast(this->GetDataNode()->GetProperty("color")); if (colorProperty.IsNotNull()) { const Color &color = colorProperty->GetColor(); m_EdgeActor->GetProperty()->SetColor(color.GetRed(), color.GetGreen(), color.GetBlue()); } else { m_EdgeActor->GetProperty()->SetColor(1.0, 1.0, 1.0); } m_ImageAssembly->SetUserTransform(this->GetDataNode()->GetVtkTransform(this->GetTimestep())); } VtkRepresentationProperty *representationProperty; this->GetDataNode()->GetProperty(representationProperty, "material.representation", renderer); if (representationProperty != nullptr) m_BackgroundActor->GetProperty()->SetRepresentation(representationProperty->GetVtkRepresentation()); } void PlaneGeometryDataVtkMapper3D::ProcessNode(DataNode *node, BaseRenderer *renderer, Surface *surface, LayerSortedActorList &layerSortedActors) { if (node != nullptr) { // we need to get the information from the 2D mapper to render the texture on the 3D plane auto *imageMapper = dynamic_cast(node->GetMapper(1)); // GetMapper(1) provides the 2D mapper for the data node // if there is a 2D mapper, which is not the standard image mapper... if (!imageMapper && node->GetMapper(1)) { //... check if it is the composite mapper std::string cname(node->GetMapper(1)->GetNameOfClass()); if (!cname.compare("CompositeMapper")) // string.compare returns 0 if the two strings are equal. { // get the standard image mapper. // This is a special case in MITK and does only work for the CompositeMapper. imageMapper = dynamic_cast(node->GetMapper(3)); } } if ((node->IsVisible(renderer)) && imageMapper) { WeakPointerProperty::Pointer rendererProp = dynamic_cast(GetDataNode()->GetPropertyList()->GetProperty("renderer")); if (rendererProp.IsNotNull()) { BaseRenderer::Pointer planeRenderer = dynamic_cast(rendererProp->GetWeakPointer().GetPointer()); // Retrieve and update image to be mapped const ImageVtkMapper2D::LocalStorage *localStorage = imageMapper->GetConstLocalStorage(planeRenderer); if (planeRenderer.IsNotNull()) { // perform update of imagemapper if needed (maybe the respective 2D renderwindow is not rendered/update // before) imageMapper->Update(planeRenderer); // If it has not been initialized already in a previous pass, // generate an actor and a texture object to // render the image associated with the ImageVtkMapper2D. vtkActor *imageActor; vtkDataSetMapper *dataSetMapper = nullptr; vtkTexture *texture; if (m_ImageActors.count(imageMapper) == 0) { dataSetMapper = vtkDataSetMapper::New(); texture = vtkNeverTranslucentTexture::New(); texture->RepeatOff(); imageActor = vtkActor::New(); imageActor->SetMapper(dataSetMapper); imageActor->SetTexture(texture); imageActor->GetProperty()->SetOpacity( 0.999); // HACK! otherwise VTK wouldn't recognize this as translucent // surface (if LUT values map to alpha < 255 // improvement: apply "opacity" property onle HERE and also in 2D image mapper. DO NOT change LUT to // achieve // translucent images (see method ChangeOpacity in image mapper 2D) // Make imageActor the sole owner of the mapper and texture // objects dataSetMapper->UnRegister(nullptr); texture->UnRegister(nullptr); // Store the actor so that it may be accessed in following // passes. m_ImageActors[imageMapper].Initialize(imageActor, imageMapper, m_ImageMapperDeletedCommand); } else { // Else, retrieve the actor and associated objects from the // previous pass. imageActor = m_ImageActors[imageMapper].m_Actor; dataSetMapper = (vtkDataSetMapper *)imageActor->GetMapper(); texture = imageActor->GetTexture(); } // Set poly data new each time its object changes (e.g. when // switching between planar and curved geometries) if ((dataSetMapper != nullptr) && (dataSetMapper->GetInput() != surface->GetVtkPolyData())) { dataSetMapper->SetInputData(surface->GetVtkPolyData()); } dataSetMapper->Update(); // Check if the m_ReslicedImage is nullptr. // This is the case when no image geometry is met by // the reslicer. In that case, the texture has to be // empty (black) and we don't have to do anything. // See fixed bug #13275 if (localStorage->m_ReslicedImage != nullptr) { texture->SetInputConnection(localStorage->m_LevelWindowFilter->GetOutputPort()); // do not use a VTK lookup table (we do that ourselves in m_LevelWindowFilter) texture->SetColorModeToDirectScalars(); auto* property3d = imageActor->GetProperty(); property3d->LightingOff(); // re-use properties from the 2D image mapper auto* property2d = localStorage->m_ImageActor->GetProperty(); property3d->SetColor(property2d->GetColor()); property3d->SetOpacity(property2d->GetOpacity()); // Set texture interpolation on/off bool textureInterpolation = node->IsOn("texture interpolation", renderer); texture->SetInterpolate(textureInterpolation); // Store this actor to be added to the actor assembly, sort // by layer int layer = 1; node->GetIntProperty("layer", layer); layerSortedActors.insert(std::pair(layer, imageActor)); } } } } } } void PlaneGeometryDataVtkMapper3D::ActorInfo::Initialize(vtkActor *actor, itk::Object *sender, itk::Command *command) { m_Actor = actor; m_Sender = sender; // Get informed when ImageMapper object is deleted, so that // the data structures built here can be deleted as well m_ObserverID = sender->AddObserver(itk::DeleteEvent(), command); } PlaneGeometryDataVtkMapper3D::ActorInfo::ActorInfo() : m_Actor(nullptr), m_Sender(nullptr), m_ObserverID(0) {} PlaneGeometryDataVtkMapper3D::ActorInfo::~ActorInfo() { if (m_Sender != nullptr) { m_Sender->RemoveObserver(m_ObserverID); } if (m_Actor != nullptr) { m_Actor->ReleaseGraphicsResources(nullptr); m_Actor->Delete(); } } } // namespace mitk diff --git a/Modules/DICOM/src/mitkDICOMImageBlockDescriptor.cpp b/Modules/DICOM/src/mitkDICOMImageBlockDescriptor.cpp index df4fa8ed0a..5bac64360b 100644 --- a/Modules/DICOM/src/mitkDICOMImageBlockDescriptor.cpp +++ b/Modules/DICOM/src/mitkDICOMImageBlockDescriptor.cpp @@ -1,913 +1,912 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #include "mitkDICOMImageBlockDescriptor.h" #include "mitkStringProperty.h" #include "mitkLevelWindowProperty.h" #include "mitkPropertyKeyPath.h" #include "mitkDICOMIOMetaInformationPropertyConstants.h" #include #include #include #include mitk::DICOMImageBlockDescriptor::DICOMImageBlockDescriptor() : m_ReaderImplementationLevel( SOPClassUnknown ) , m_PropertyList( PropertyList::New() ) , m_TagCache( nullptr ) , m_PropertiesOutOfDate( true ) { m_PropertyFunctor = &mitk::DICOMImageBlockDescriptor::GetPropertyForDICOMValues; } mitk::DICOMImageBlockDescriptor::~DICOMImageBlockDescriptor() { } mitk::DICOMImageBlockDescriptor::DICOMImageBlockDescriptor( const DICOMImageBlockDescriptor& other ) : m_ImageFrameList( other.m_ImageFrameList ) , m_MitkImage( other.m_MitkImage ) , m_SliceIsLoaded( other.m_SliceIsLoaded ) , m_ReaderImplementationLevel( other.m_ReaderImplementationLevel ) , m_TiltInformation( other.m_TiltInformation ) , m_PropertyList( other.m_PropertyList->Clone() ) , m_TagCache( other.m_TagCache ) , m_PropertiesOutOfDate( other.m_PropertiesOutOfDate ) , m_AdditionalTagMap(other.m_AdditionalTagMap) , m_FoundAdditionalTags(other.m_FoundAdditionalTags) , m_PropertyFunctor(other.m_PropertyFunctor) { if ( m_MitkImage ) { m_MitkImage = m_MitkImage->Clone(); } } mitk::DICOMImageBlockDescriptor& mitk::DICOMImageBlockDescriptor:: operator=( const DICOMImageBlockDescriptor& other ) { if ( this != &other ) { m_ImageFrameList = other.m_ImageFrameList; m_MitkImage = other.m_MitkImage; m_SliceIsLoaded = other.m_SliceIsLoaded; m_ReaderImplementationLevel = other.m_ReaderImplementationLevel; m_TiltInformation = other.m_TiltInformation; m_AdditionalTagMap = other.m_AdditionalTagMap; m_FoundAdditionalTags = other.m_FoundAdditionalTags; m_PropertyFunctor = other.m_PropertyFunctor; if ( other.m_PropertyList ) { m_PropertyList = other.m_PropertyList->Clone(); } if ( other.m_MitkImage ) { m_MitkImage = other.m_MitkImage->Clone(); } m_TagCache = other.m_TagCache; m_PropertiesOutOfDate = other.m_PropertiesOutOfDate; } return *this; } mitk::DICOMTagList mitk::DICOMImageBlockDescriptor::GetTagsOfInterest() { DICOMTagList completeList; completeList.push_back( DICOMTag( 0x0018, 0x1164 ) ); // pixel spacing completeList.push_back( DICOMTag( 0x0028, 0x0030 ) ); // imager pixel spacing completeList.push_back( DICOMTag( 0x0008, 0x0018 ) ); // sop instance UID completeList.push_back( DICOMTag( 0x0008, 0x0016 ) ); // sop class UID completeList.push_back( DICOMTag( 0x0020, 0x0011 ) ); // series number completeList.push_back( DICOMTag( 0x0008, 0x1030 ) ); // study description completeList.push_back( DICOMTag( 0x0008, 0x103e ) ); // series description completeList.push_back( DICOMTag( 0x0008, 0x0060 ) ); // modality completeList.push_back( DICOMTag( 0x0018, 0x0024 ) ); // sequence name completeList.push_back( DICOMTag( 0x0020, 0x0037 ) ); // image orientation completeList.push_back( DICOMTag( 0x0020, 0x1041 ) ); // slice location completeList.push_back( DICOMTag( 0x0020, 0x0012 ) ); // acquisition number completeList.push_back( DICOMTag( 0x0020, 0x0013 ) ); // instance number completeList.push_back( DICOMTag( 0x0020, 0x0032 ) ); // image position patient completeList.push_back( DICOMTag( 0x0028, 0x1050 ) ); // window center completeList.push_back( DICOMTag( 0x0028, 0x1051 ) ); // window width completeList.push_back( DICOMTag( 0x0008, 0x0008 ) ); // image type completeList.push_back( DICOMTag( 0x0028, 0x0004 ) ); // photometric interpretation return completeList; } void mitk::DICOMImageBlockDescriptor::SetAdditionalTagsOfInterest( const AdditionalTagsMapType& tagMap) { m_AdditionalTagMap = tagMap; } void mitk::DICOMImageBlockDescriptor::SetTiltInformation( const GantryTiltInformation& info ) { m_TiltInformation = info; } const mitk::GantryTiltInformation mitk::DICOMImageBlockDescriptor::GetTiltInformation() const { return m_TiltInformation; } void mitk::DICOMImageBlockDescriptor::SetImageFrameList( const DICOMImageFrameList& framelist ) { m_ImageFrameList = framelist; m_SliceIsLoaded.resize( framelist.size() ); m_SliceIsLoaded.assign( framelist.size(), false ); m_PropertiesOutOfDate = true; } const mitk::DICOMImageFrameList& mitk::DICOMImageBlockDescriptor::GetImageFrameList() const { return m_ImageFrameList; } void mitk::DICOMImageBlockDescriptor::SetMitkImage( Image::Pointer image ) { if ( m_MitkImage != image ) { if ( m_TagCache.IsExpired() ) { MITK_ERROR << "Unable to describe MITK image with properties without a tag-cache object!"; m_MitkImage = nullptr; return; } if ( m_ImageFrameList.empty() ) { MITK_ERROR << "Unable to describe MITK image with properties without a frame list!"; m_MitkImage = nullptr; return; } // Should verify that the image matches m_ImageFrameList and m_TagCache // however, this is hard to do without re-analyzing all // TODO we should at least make sure that the number of frames is identical (plus rows/columns, // orientation) // without gantry tilt correction, we can also check image origin m_MitkImage = this->DescribeImageWithProperties( this->FixupSpacing( image ) ); } } mitk::Image::Pointer mitk::DICOMImageBlockDescriptor::GetMitkImage() const { return m_MitkImage; } mitk::Image::Pointer mitk::DICOMImageBlockDescriptor::FixupSpacing( Image* mitkImage ) { if ( mitkImage ) { Vector3D imageSpacing = mitkImage->GetGeometry()->GetSpacing(); ScalarType desiredSpacingX = imageSpacing[0]; ScalarType desiredSpacingY = imageSpacing[1]; this->GetDesiredMITKImagePixelSpacing( desiredSpacingX, desiredSpacingY ); // prefer pixel spacing over imager pixel spacing if ( desiredSpacingX <= 0 || desiredSpacingY <= 0 ) { return mitkImage; } MITK_DEBUG << "Loaded image with spacing " << imageSpacing[0] << ", " << imageSpacing[1]; MITK_DEBUG << "Found correct spacing info " << desiredSpacingX << ", " << desiredSpacingY; imageSpacing[0] = desiredSpacingX; imageSpacing[1] = desiredSpacingY; mitkImage->GetGeometry()->SetSpacing( imageSpacing ); } return mitkImage; } void mitk::DICOMImageBlockDescriptor::SetSliceIsLoaded( unsigned int index, bool isLoaded ) { if ( index < m_SliceIsLoaded.size() ) { m_SliceIsLoaded[index] = isLoaded; } else { std::stringstream ss; ss << "Index " << index << " out of range (" << m_SliceIsLoaded.size() << " indices reserved)"; throw std::invalid_argument( ss.str() ); } } bool mitk::DICOMImageBlockDescriptor::IsSliceLoaded( unsigned int index ) const { if ( index < m_SliceIsLoaded.size() ) { return m_SliceIsLoaded[index]; } else { std::stringstream ss; ss << "Index " << index << " out of range (" << m_SliceIsLoaded.size() << " indices reserved)"; throw std::invalid_argument( ss.str() ); } } bool mitk::DICOMImageBlockDescriptor::AllSlicesAreLoaded() const { bool allLoaded = true; for ( auto iter = m_SliceIsLoaded.cbegin(); iter != m_SliceIsLoaded.cend(); ++iter ) { allLoaded &= *iter; } return allLoaded; } /* PS defined IPS defined PS==IPS 0 0 --> UNKNOWN spacing, loader will invent 0 1 --> spacing as at detector surface 1 0 --> spacing as in patient 1 1 0 --> detector surface spacing CORRECTED for geometrical magnifications: spacing as in patient 1 1 1 --> detector surface spacing NOT corrected for geometrical magnifications: spacing as at detector */ mitk::PixelSpacingInterpretation mitk::DICOMImageBlockDescriptor::GetPixelSpacingInterpretation() const { if ( m_ImageFrameList.empty() || m_TagCache.IsExpired() ) { MITK_ERROR << "Invalid call to GetPixelSpacingInterpretation. Need to have initialized tag-cache!"; return SpacingUnknown; } const std::string pixelSpacing = this->GetPixelSpacing(); const std::string imagerPixelSpacing = this->GetImagerPixelSpacing(); if ( pixelSpacing.empty() ) { if ( imagerPixelSpacing.empty() ) { return SpacingUnknown; } else { return SpacingAtDetector; } } else // Pixel Spacing defined { if ( imagerPixelSpacing.empty() ) { return SpacingInPatient; } else if ( pixelSpacing != imagerPixelSpacing ) { return SpacingInPatient; } else { return SpacingAtDetector; } } } std::string mitk::DICOMImageBlockDescriptor::GetPixelSpacing() const { - if ( m_ImageFrameList.empty() || m_TagCache.IsExpired() ) + auto tagCache = m_TagCache.Lock(); + + if ( m_ImageFrameList.empty() || tagCache.IsNull() ) { MITK_ERROR << "Invalid call to GetPixelSpacing. Need to have initialized tag-cache!"; return std::string( "" ); } static const DICOMTag tagPixelSpacing( 0x0028, 0x0030 ); - return m_TagCache.Lock()->GetTagValue( m_ImageFrameList.front(), tagPixelSpacing ).value; + return tagCache->GetTagValue( m_ImageFrameList.front(), tagPixelSpacing ).value; } std::string mitk::DICOMImageBlockDescriptor::GetImagerPixelSpacing() const { - if ( m_ImageFrameList.empty() || m_TagCache.IsExpired() ) + auto tagCache = m_TagCache.Lock(); + + if ( m_ImageFrameList.empty() || tagCache.IsNull() ) { MITK_ERROR << "Invalid call to GetImagerPixelSpacing. Need to have initialized tag-cache!"; return std::string( "" ); } static const DICOMTag tagImagerPixelSpacing( 0x0018, 0x1164 ); - return m_TagCache.Lock()->GetTagValue( m_ImageFrameList.front(), tagImagerPixelSpacing ).value; + return tagCache->GetTagValue( m_ImageFrameList.front(), tagImagerPixelSpacing ).value; } void mitk::DICOMImageBlockDescriptor::GetDesiredMITKImagePixelSpacing( ScalarType& spacingX, ScalarType& spacingY ) const { const std::string pixelSpacing = this->GetPixelSpacing(); // preference for "in patient" pixel spacing if ( !DICOMStringToSpacing( pixelSpacing, spacingX, spacingY ) ) { const std::string imagerPixelSpacing = this->GetImagerPixelSpacing(); // fallback to "on detector" spacing if ( !DICOMStringToSpacing( imagerPixelSpacing, spacingX, spacingY ) ) { // at this point we have no hints whether the spacing is correct // do a quick sanity check and either trust in the input or set both to 1 // We assume neither spacing to be negative, zero or unexpectedly large for // medical images if (spacingX < mitk::eps || spacingX > 1000 || spacingY < mitk::eps || spacingY > 1000) { spacingX = spacingY = 1.0; } } } } void mitk::DICOMImageBlockDescriptor::SetProperty( const std::string& key, BaseProperty* value ) { m_PropertyList->SetProperty( key, value ); } mitk::BaseProperty* mitk::DICOMImageBlockDescriptor::GetProperty( const std::string& key ) const { this->UpdateImageDescribingProperties(); return m_PropertyList->GetProperty( key ); } std::string mitk::DICOMImageBlockDescriptor::GetPropertyAsString( const std::string& key ) const { this->UpdateImageDescribingProperties(); const mitk::BaseProperty::Pointer property = m_PropertyList->GetProperty( key ); if ( property.IsNotNull() ) { return property->GetValueAsString(); } else { return std::string( "" ); } } void mitk::DICOMImageBlockDescriptor::SetFlag( const std::string& key, bool value ) { m_PropertyList->ReplaceProperty( key, BoolProperty::New( value ) ); } bool mitk::DICOMImageBlockDescriptor::GetFlag( const std::string& key, bool defaultValue ) const { this->UpdateImageDescribingProperties(); BoolProperty::ConstPointer boolProp = dynamic_cast( this->GetProperty( key ) ); if ( boolProp.IsNotNull() ) { return boolProp->GetValue(); } else { return defaultValue; } } void mitk::DICOMImageBlockDescriptor::SetIntProperty( const std::string& key, int value ) { m_PropertyList->ReplaceProperty( key, IntProperty::New( value ) ); } int mitk::DICOMImageBlockDescriptor::GetIntProperty( const std::string& key, int defaultValue ) const { this->UpdateImageDescribingProperties(); IntProperty::ConstPointer intProp = dynamic_cast( this->GetProperty( key ) ); if ( intProp.IsNotNull() ) { return intProp->GetValue(); } else { return defaultValue; } } double mitk::DICOMImageBlockDescriptor::stringtodouble( const std::string& str ) const { double d; std::string trimmedstring( str ); try { trimmedstring = trimmedstring.erase( trimmedstring.find_last_not_of( " \n\r\t" ) + 1 ); } catch ( ... ) { // no last not of } std::string firstcomponent( trimmedstring ); try { firstcomponent = trimmedstring.erase( trimmedstring.find_first_of( "\\" ) ); } catch ( ... ) { // no last not of } std::istringstream converter( firstcomponent ); if ( !firstcomponent.empty() && ( converter >> d ) && converter.eof() ) { return d; } else { throw std::invalid_argument( "Argument is not a convertable number" ); } } mitk::Image::Pointer mitk::DICOMImageBlockDescriptor::DescribeImageWithProperties( Image* mitkImage ) { // TODO: this is a collection of properties that have been provided by the // legacy DicomSeriesReader. // We should at some point clean up this collection and name them in a more // consistent way! if ( !mitkImage ) return mitkImage; mitkImage->SetProperty(PropertyKeyPathToPropertyName(DICOMIOMetaInformationPropertyConstants::READER_FILES()), this->GetProperty("filenamesForSlices")); mitkImage->SetProperty(PropertyKeyPathToPropertyName(DICOMIOMetaInformationPropertyConstants::READER_PIXEL_SPACING_INTERPRETATION_STRING()), StringProperty::New(PixelSpacingInterpretationToString(this->GetPixelSpacingInterpretation()))); mitkImage->SetProperty(PropertyKeyPathToPropertyName(DICOMIOMetaInformationPropertyConstants::READER_PIXEL_SPACING_INTERPRETATION()), GenericProperty::New(this->GetPixelSpacingInterpretation())); mitkImage->SetProperty(PropertyKeyPathToPropertyName(DICOMIOMetaInformationPropertyConstants::READER_IMPLEMENTATION_LEVEL_STRING()), StringProperty::New(ReaderImplementationLevelToString(m_ReaderImplementationLevel))); mitkImage->SetProperty(PropertyKeyPathToPropertyName(DICOMIOMetaInformationPropertyConstants::READER_IMPLEMENTATION_LEVEL()), GenericProperty::New(m_ReaderImplementationLevel)); mitkImage->SetProperty(PropertyKeyPathToPropertyName(DICOMIOMetaInformationPropertyConstants::READER_GANTRY_TILT_CORRECTED()), BoolProperty::New(this->GetTiltInformation().IsRegularGantryTilt())); mitkImage->SetProperty(PropertyKeyPathToPropertyName(DICOMIOMetaInformationPropertyConstants::READER_3D_plus_t()), BoolProperty::New(this->GetFlag("3D+t", false))); mitkImage->SetProperty(PropertyKeyPathToPropertyName(DICOMIOMetaInformationPropertyConstants::READER_GDCM()), StringProperty::New(gdcm::Version::GetVersion())); mitkImage->SetProperty(PropertyKeyPathToPropertyName(DICOMIOMetaInformationPropertyConstants::READER_DCMTK()), StringProperty::New(PACKAGE_VERSION)); // get all found additional tags of interest for (const auto &tag : m_FoundAdditionalTags) { BaseProperty* prop = this->GetProperty(tag); if (prop) { mitkImage->SetProperty(tag.c_str(), prop); } } ///////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////// //// Deprecated properties should be removed sooner then later (see above) ///////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////// // first part: add some tags that describe individual slices // these propeties are defined at analysis time (see UpdateImageDescribingProperties()) const char* propertyKeySliceLocation = "dicom.image.0020.1041"; const char* propertyKeyInstanceNumber = "dicom.image.0020.0013"; const char* propertyKeySOPInstanceUID = "dicom.image.0008.0018"; mitkImage->SetProperty( propertyKeySliceLocation, this->GetProperty( "sliceLocationForSlices" ) ); mitkImage->SetProperty( propertyKeyInstanceNumber, this->GetProperty( "instanceNumberForSlices" ) ); mitkImage->SetProperty( propertyKeySOPInstanceUID, this->GetProperty( "SOPInstanceUIDForSlices" ) ); mitkImage->SetProperty( "files", this->GetProperty( "filenamesForSlices_deprecated" ) ); // second part: add properties that describe the whole image block mitkImage->SetProperty( "dicomseriesreader.SOPClassUID", StringProperty::New( this->GetSOPClassUID() ) ); mitkImage->SetProperty( "dicomseriesreader.SOPClass", StringProperty::New( this->GetSOPClassUIDAsName() ) ); mitkImage->SetProperty( "dicomseriesreader.PixelSpacingInterpretationString", StringProperty::New( PixelSpacingInterpretationToString( this->GetPixelSpacingInterpretation() ) ) ); mitkImage->SetProperty( "dicomseriesreader.PixelSpacingInterpretation", GenericProperty::New( this->GetPixelSpacingInterpretation() ) ); mitkImage->SetProperty( "dicomseriesreader.ReaderImplementationLevelString", StringProperty::New( ReaderImplementationLevelToString( m_ReaderImplementationLevel ) ) ); mitkImage->SetProperty( "dicomseriesreader.ReaderImplementationLevel", GenericProperty::New( m_ReaderImplementationLevel ) ); mitkImage->SetProperty( "dicomseriesreader.GantyTiltCorrected", BoolProperty::New( this->GetTiltInformation().IsRegularGantryTilt() ) ); mitkImage->SetProperty( "dicomseriesreader.3D+t", BoolProperty::New( this->GetFlag( "3D+t", false ) ) ); // level window const std::string windowCenter = this->GetPropertyAsString( "windowCenter" ); const std::string windowWidth = this->GetPropertyAsString( "windowWidth" ); try { const double level = stringtodouble( windowCenter ); const double window = stringtodouble( windowWidth ); mitkImage->SetProperty( "levelwindow", LevelWindowProperty::New( LevelWindow( level, window ) ) ); } catch ( ... ) { // nothing, no levelwindow to be predicted... } const std::string modality = this->GetPropertyAsString( "modality" ); mitkImage->SetProperty( "modality", StringProperty::New( modality ) ); mitkImage->SetProperty( "dicom.pixel.PhotometricInterpretation", this->GetProperty( "photometricInterpretation" ) ); mitkImage->SetProperty( "dicom.image.imagetype", this->GetProperty( "imagetype" ) ); mitkImage->SetProperty( "dicom.study.StudyDescription", this->GetProperty( "studyDescription" ) ); mitkImage->SetProperty( "dicom.series.SeriesDescription", this->GetProperty( "seriesDescription" ) ); mitkImage->SetProperty( "dicom.pixel.Rows", this->GetProperty( "rows" ) ); mitkImage->SetProperty( "dicom.pixel.Columns", this->GetProperty( "columns" ) ); // fourth part: get something from ImageIO. BUT this needs to be created elsewhere. or not at all! return mitkImage; } void mitk::DICOMImageBlockDescriptor::SetReaderImplementationLevel( const ReaderImplementationLevel& level ) { m_ReaderImplementationLevel = level; } mitk::ReaderImplementationLevel mitk::DICOMImageBlockDescriptor::GetReaderImplementationLevel() const { return m_ReaderImplementationLevel; } std::string mitk::DICOMImageBlockDescriptor::GetSOPClassUID() const { - if ( !m_ImageFrameList.empty() && !m_TagCache.IsExpired() ) + auto tagCache = m_TagCache.Lock(); + + if ( !m_ImageFrameList.empty() && tagCache.IsNotNull() ) { static const DICOMTag tagSOPClassUID( 0x0008, 0x0016 ); - return m_TagCache.Lock()->GetTagValue( m_ImageFrameList.front(), tagSOPClassUID ).value; + return tagCache->GetTagValue( m_ImageFrameList.front(), tagSOPClassUID ).value; } else { MITK_ERROR << "Invalid call to DICOMImageBlockDescriptor::GetSOPClassUID(). Need to have initialized tag-cache!"; return std::string( "" ); } } std::string mitk::DICOMImageBlockDescriptor::GetSOPClassUIDAsName() const { if ( !m_ImageFrameList.empty() && !m_TagCache.IsExpired() ) { gdcm::UIDs uidKnowledge; uidKnowledge.SetFromUID( this->GetSOPClassUID().c_str() ); const char* name = uidKnowledge.GetName(); if ( name ) { return std::string( name ); } else { return std::string( "" ); } } else { MITK_ERROR << "Invalid call to DICOMImageBlockDescriptor::GetSOPClassUIDAsName(). Need to have " "initialized tag-cache!"; return std::string( "" ); } } int mitk::DICOMImageBlockDescriptor::GetNumberOfTimeSteps() const { int result = 1; this->m_PropertyList->GetIntProperty("timesteps", result); return result; }; int mitk::DICOMImageBlockDescriptor::GetNumberOfFramesPerTimeStep() const { const int numberOfTimesteps = this->GetNumberOfTimeSteps(); int numberOfFramesPerTimestep = this->m_ImageFrameList.size() / numberOfTimesteps; assert(int(double((double)this->m_ImageFrameList.size() / (double)numberOfTimesteps)) == numberOfFramesPerTimestep); // this should hold return numberOfFramesPerTimestep; }; void mitk::DICOMImageBlockDescriptor::SetTagCache( DICOMTagCache* privateCache ) { // this must only be used during loading and never afterwards m_TagCache = privateCache; } #define printPropertyRange( label, property_name ) \ \ { \ const std::string first = this->GetPropertyAsString( #property_name "First" ); \ const std::string last = this->GetPropertyAsString( #property_name "Last" ); \ if ( !first.empty() || !last.empty() ) \ { \ if ( first == last ) \ { \ os << " " label ": '" << first << "'" << std::endl; \ } \ else \ { \ os << " " label ": '" << first << "' - '" << last << "'" << std::endl; \ } \ } \ \ } #define printProperty( label, property_name ) \ \ { \ const std::string first = this->GetPropertyAsString( #property_name ); \ if ( !first.empty() ) \ { \ os << " " label ": '" << first << "'" << std::endl; \ } \ \ } #define printBool( label, commands ) \ \ { \ os << " " label ": '" << ( commands ? "yes" : "no" ) << "'" << std::endl; \ \ } void mitk::DICOMImageBlockDescriptor::Print(std::ostream& os, bool filenameDetails) const { os << " Number of Frames: '" << m_ImageFrameList.size() << "'" << std::endl; os << " SOP class: '" << this->GetSOPClassUIDAsName() << "'" << std::endl; printProperty( "Series Number", seriesNumber ); printProperty( "Study Description", studyDescription ); printProperty( "Series Description", seriesDescription ); printProperty( "Modality", modality ); printProperty( "Sequence Name", sequenceName ); printPropertyRange( "Slice Location", sliceLocation ); printPropertyRange( "Acquisition Number", acquisitionNumber ); printPropertyRange( "Instance Number", instanceNumber ); printPropertyRange( "Image Position", imagePositionPatient ); printProperty( "Image Orientation", orientation ); os << " Pixel spacing interpretation: '" << PixelSpacingInterpretationToString( this->GetPixelSpacingInterpretation() ) << "'" << std::endl; printBool( "Gantry Tilt", this->GetTiltInformation().IsRegularGantryTilt() ) // printBool("3D+t", this->GetFlag("3D+t",false)) // os << " MITK image loaded: '" << (this->GetMitkImage().IsNotNull() ? "yes" : "no") << "'" << // std::endl; if ( filenameDetails ) { os << " Files in this image block:" << std::endl; for ( auto frameIter = m_ImageFrameList.begin(); frameIter != m_ImageFrameList.end(); ++frameIter ) { os << " " << ( *frameIter )->Filename; if ( ( *frameIter )->FrameNo > 0 ) { os << ", " << ( *frameIter )->FrameNo; } os << std::endl; } } } #define storeTagValueToProperty( tag_name, tag_g, tag_e ) \ \ { \ const DICOMTag t( tag_g, tag_e ); \ const std::string tagValue = tagCache->GetTagValue( firstFrame, t ).value; \ const_cast( this ) \ ->SetProperty( #tag_name, StringProperty::New( tagValue ) ); \ \ } #define storeTagValueRangeToProperty( tag_name, tag_g, tag_e ) \ \ { \ const DICOMTag t( tag_g, tag_e ); \ const std::string tagValueFirst = tagCache->GetTagValue( firstFrame, t ).value; \ const std::string tagValueLast = tagCache->GetTagValue( lastFrame, t ).value; \ const_cast( this ) \ ->SetProperty( #tag_name "First", StringProperty::New( tagValueFirst ) ); \ const_cast( this ) \ ->SetProperty( #tag_name "Last", StringProperty::New( tagValueLast ) ); \ \ } void mitk::DICOMImageBlockDescriptor::UpdateImageDescribingProperties() const { if ( !m_PropertiesOutOfDate ) return; if ( !m_ImageFrameList.empty() ) { - if ( m_TagCache.IsExpired() ) - { - MITK_ERROR << "Invalid call to DICOMImageBlockDescriptor::UpdateImageDescribingProperties(). Need to " - "have initialized tag-cache!"; - return; - } - auto tagCache = m_TagCache.Lock(); if (tagCache.IsNull()) { MITK_ERROR << "Invalid call to DICOMImageBlockDescriptor::UpdateImageDescribingProperties(). Need to " "have initialized tag-cache!"; return; } const DICOMImageFrameInfo::Pointer firstFrame = m_ImageFrameList.front(); const DICOMImageFrameInfo::Pointer lastFrame = m_ImageFrameList.back(); // see macros above storeTagValueToProperty( seriesNumber, 0x0020, 0x0011 ); storeTagValueToProperty( studyDescription, 0x0008, 0x1030 ); storeTagValueToProperty( seriesDescription, 0x0008, 0x103e ); storeTagValueToProperty( modality, 0x0008, 0x0060 ); storeTagValueToProperty( sequenceName, 0x0018, 0x0024 ); storeTagValueToProperty( orientation, 0x0020, 0x0037 ); storeTagValueToProperty( rows, 0x0028, 0x0010 ); storeTagValueToProperty( columns, 0x0028, 0x0011 ); storeTagValueRangeToProperty( sliceLocation, 0x0020, 0x1041 ); storeTagValueRangeToProperty( acquisitionNumber, 0x0020, 0x0012 ); storeTagValueRangeToProperty( instanceNumber, 0x0020, 0x0013 ); storeTagValueRangeToProperty( imagePositionPatient, 0x0020, 0x0032 ); storeTagValueToProperty( windowCenter, 0x0028, 0x1050 ); storeTagValueToProperty( windowWidth, 0x0028, 0x1051 ); storeTagValueToProperty( imageType, 0x0008, 0x0008 ); storeTagValueToProperty( photometricInterpretation, 0x0028, 0x0004 ); // some per-image attributes // frames are just numbered starting from 0. timestep 1 (the second time-step) has frames starting at // (number-of-frames-per-timestep) // std::string propertyKeySliceLocation = "dicom.image.0020.1041"; // std::string propertyKeyInstanceNumber = "dicom.image.0020.0013"; // std::string propertyKeySOPInstanceNumber = "dicom.image.0008.0018"; StringLookupTable sliceLocationForSlices; StringLookupTable instanceNumberForSlices; StringLookupTable SOPInstanceUIDForSlices; StringLookupTable filenamesForSlices_deprecated; DICOMCachedValueLookupTable filenamesForSlices; const DICOMTag tagSliceLocation( 0x0020, 0x1041 ); const DICOMTag tagInstanceNumber( 0x0020, 0x0013 ); const DICOMTag tagSOPInstanceNumber( 0x0008, 0x0018 ); std::unordered_map additionalTagResultList; unsigned int slice(0); int timePoint(-1); const int framesPerTimeStep = this->GetNumberOfFramesPerTimeStep(); for ( auto frameIter = m_ImageFrameList.begin(); frameIter != m_ImageFrameList.end(); ++slice, ++frameIter ) { unsigned int zSlice = slice%framesPerTimeStep; if ( zSlice == 0) { timePoint++; } const std::string sliceLocation = tagCache->GetTagValue( *frameIter, tagSliceLocation ).value; sliceLocationForSlices.SetTableValue( slice, sliceLocation ); const std::string instanceNumber = tagCache->GetTagValue( *frameIter, tagInstanceNumber ).value; instanceNumberForSlices.SetTableValue( slice, instanceNumber ); const std::string sopInstanceUID = tagCache->GetTagValue( *frameIter, tagSOPInstanceNumber ).value; SOPInstanceUIDForSlices.SetTableValue( slice, sopInstanceUID ); const std::string filename = ( *frameIter )->Filename; filenamesForSlices_deprecated.SetTableValue( slice, filename ); filenamesForSlices.SetTableValue(slice, { static_cast(timePoint), zSlice, filename }); MITK_DEBUG << "Tag info for slice " << slice << ": SL '" << sliceLocation << "' IN '" << instanceNumber << "' SOP instance UID '" << sopInstanceUID << "'"; for (const auto& tag : m_AdditionalTagMap) { const DICOMTagCache::FindingsListType findings = tagCache->GetTagValue( *frameIter, tag.first ); for (const auto& finding : findings) { if (finding.isValid) { std::string propKey = (tag.second.empty()) ? DICOMTagPathToPropertyName(finding.path) : tag.second; DICOMCachedValueInfo info{ static_cast(timePoint), zSlice, finding.value }; additionalTagResultList[propKey].SetTableValue(slice, info); } } } } // add property or properties with proper names auto* thisInstance = const_cast( this ); thisInstance->SetProperty( "sliceLocationForSlices", StringLookupTableProperty::New( sliceLocationForSlices ) ); thisInstance->SetProperty( "instanceNumberForSlices", StringLookupTableProperty::New( instanceNumberForSlices ) ); thisInstance->SetProperty( "SOPInstanceUIDForSlices", StringLookupTableProperty::New( SOPInstanceUIDForSlices ) ); thisInstance->SetProperty( "filenamesForSlices_deprecated", StringLookupTableProperty::New( filenamesForSlices_deprecated ) ); thisInstance->SetProperty("filenamesForSlices", m_PropertyFunctor(filenamesForSlices)); //add properties for additional tags of interest for ( auto iter = additionalTagResultList.cbegin(); iter != additionalTagResultList.cend(); ++iter ) { thisInstance->SetProperty( iter->first, m_PropertyFunctor( iter->second ) ); thisInstance->m_FoundAdditionalTags.insert(m_FoundAdditionalTags.cend(),iter->first); } m_PropertiesOutOfDate = false; } } mitk::BaseProperty::Pointer mitk::DICOMImageBlockDescriptor::GetPropertyForDICOMValues(const DICOMCachedValueLookupTable& cacheLookupTable) { const auto& lookupTable = cacheLookupTable.GetLookupTable(); typedef std::pair PairType; if ( std::adjacent_find( lookupTable.cbegin(), lookupTable.cend(), []( const PairType& lhs, const PairType& rhs ) { return lhs.second.Value != rhs.second.Value; } ) == lookupTable.cend() ) { return static_cast( mitk::StringProperty::New(cacheLookupTable.GetTableValue(0).Value).GetPointer()); } StringLookupTable stringTable; for (const auto &element : lookupTable) { stringTable.SetTableValue(element.first, element.second.Value); } return static_cast( mitk::StringLookupTableProperty::New(stringTable).GetPointer()); } void mitk::DICOMImageBlockDescriptor::SetTagLookupTableToPropertyFunctor( TagLookupTableToPropertyFunctor functor ) { if ( functor != nullptr ) { m_PropertyFunctor = functor; } } mitk::BaseProperty::ConstPointer mitk::DICOMImageBlockDescriptor::GetConstProperty(const std::string &propertyKey, const std::string &/*contextName*/, bool /*fallBackOnDefaultContext*/) const { this->UpdateImageDescribingProperties(); return m_PropertyList->GetConstProperty(propertyKey); }; std::vector mitk::DICOMImageBlockDescriptor::GetPropertyKeys(const std::string &/*contextName*/, bool /*includeDefaultContext*/) const { this->UpdateImageDescribingProperties(); return m_PropertyList->GetPropertyKeys(); }; std::vector mitk::DICOMImageBlockDescriptor::GetPropertyContextNames() const { return std::vector(); }; diff --git a/Modules/QtWidgets/include/QmitkAbstractDataStorageModel.h b/Modules/QtWidgets/include/QmitkAbstractDataStorageModel.h index 5d4f0f9123..d267484d6b 100644 --- a/Modules/QtWidgets/include/QmitkAbstractDataStorageModel.h +++ b/Modules/QtWidgets/include/QmitkAbstractDataStorageModel.h @@ -1,88 +1,88 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #ifndef QMITKABSTRACTDATASTORAGEMODEL_H #define QMITKABSTRACTDATASTORAGEMODEL_H #include // mitk core #include #include #include // qt #include /* * @brief This abstract class extends the 'QAbstractItemModel' to accept an 'mitk::DataStorage' and a 'mitk::NodePredicateBase'. * It registers itself as a node event listener of the data storage. * The 'QmitkAbstractDataStorageModel' provides three empty functions, 'NodeAdded', 'NodeChanged' and 'NodeRemoved', that * may be implemented by subclasses. These functions allow to react to the 'AddNodeEvent', 'ChangedNodeEvent' and * 'RemoveNodeEvent' of the data storage. This might be useful to force an update on a custom view to correctly * represent the content of the data storage. * * A concrete implementation of this class is used to store the temporarily shown data nodes of the data storage. * These nodes may be a subset of all the nodes inside the data storage, if a specific node predicate is set. * * A model that implements this class has to return mitk::DataNode::Pointer objects for model indexes when the * role is QmitkDataNodeRole. */ class MITKQTWIDGETS_EXPORT QmitkAbstractDataStorageModel : public QAbstractItemModel { Q_OBJECT public: ~QmitkAbstractDataStorageModel() override; /* * @brief Sets the data storage and adds listener for node events. * * @param dataStorage A pointer to the data storage to set. */ void SetDataStorage(mitk::DataStorage* dataStorage); - mitk::DataStorage* GetDataStorage() const; + mitk::DataStorage::Pointer GetDataStorage() const; /* * @brief Sets the node predicate and updates the model data, according to the node predicate. * * @param nodePredicate A pointer to node predicate. */ void SetNodePredicate(const mitk::NodePredicateBase* nodePredicate); const mitk::NodePredicateBase* GetNodePredicate() const { return m_NodePredicate; } protected: virtual void DataStorageChanged() = 0; virtual void NodePredicateChanged() = 0; virtual void NodeAdded(const mitk::DataNode* node) = 0; virtual void NodeChanged(const mitk::DataNode* node) = 0; virtual void NodeRemoved(const mitk::DataNode* node) = 0; QmitkAbstractDataStorageModel(QObject* parent = nullptr); QmitkAbstractDataStorageModel(mitk::DataStorage* dataStorage, QObject* parent = nullptr); mitk::WeakPointer m_DataStorage; mitk::NodePredicateBase::ConstPointer m_NodePredicate; private: /** Helper triggered on the storage delete event */ void SetDataStorageDeleted(); unsigned long m_DataStorageDeletedTag; }; #endif // QMITKABSTRACTDATASTORAGEMODEL_H diff --git a/Modules/QtWidgets/src/QmitkAbstractDataStorageInspector.cpp b/Modules/QtWidgets/src/QmitkAbstractDataStorageInspector.cpp index 96efdd6282..61ad49c2c9 100644 --- a/Modules/QtWidgets/src/QmitkAbstractDataStorageInspector.cpp +++ b/Modules/QtWidgets/src/QmitkAbstractDataStorageInspector.cpp @@ -1,79 +1,80 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #include QmitkAbstractDataStorageInspector::QmitkAbstractDataStorageInspector(QWidget* parent/* = nullptr*/) : QWidget(parent) , m_NodePredicate(nullptr) { m_Connector = std::make_unique(); connect(m_Connector.get(), &QmitkModelViewSelectionConnector::CurrentSelectionChanged, this, &QmitkAbstractDataStorageInspector::OnSelectionChanged); } QmitkAbstractDataStorageInspector::~QmitkAbstractDataStorageInspector() { } void QmitkAbstractDataStorageInspector::SetDataStorage(mitk::DataStorage* dataStorage) { if (m_DataStorage != dataStorage) { m_DataStorage = dataStorage; + auto lockedDataStorage = m_DataStorage.Lock(); - if (!m_DataStorage.IsExpired()) + if (lockedDataStorage.IsNotNull()) { this->Initialize(); } } } void QmitkAbstractDataStorageInspector::SetNodePredicate(const mitk::NodePredicateBase* nodePredicate) { if (m_NodePredicate != nodePredicate) { m_NodePredicate = nodePredicate; this->Initialize(); } } const mitk::NodePredicateBase* QmitkAbstractDataStorageInspector::GetNodePredicate() const { return m_NodePredicate; } QmitkAbstractDataStorageInspector::NodeList QmitkAbstractDataStorageInspector::GetSelectedNodes() const { return m_Connector->GetSelectedNodes(); } bool QmitkAbstractDataStorageInspector::GetSelectOnlyVisibleNodes() const { return m_Connector->GetSelectOnlyVisibleNodes(); } void QmitkAbstractDataStorageInspector::SetSelectOnlyVisibleNodes(bool selectOnlyVisibleNodes) { m_Connector->SetSelectOnlyVisibleNodes(selectOnlyVisibleNodes); } void QmitkAbstractDataStorageInspector::SetCurrentSelection(NodeList selectedNodes) { m_Connector->SetCurrentSelection(selectedNodes); } void QmitkAbstractDataStorageInspector::OnSelectionChanged(NodeList selectedNodes) { emit CurrentSelectionChanged(selectedNodes); } diff --git a/Modules/QtWidgets/src/QmitkAbstractDataStorageModel.cpp b/Modules/QtWidgets/src/QmitkAbstractDataStorageModel.cpp index 5718d7e938..719a96cf37 100644 --- a/Modules/QtWidgets/src/QmitkAbstractDataStorageModel.cpp +++ b/Modules/QtWidgets/src/QmitkAbstractDataStorageModel.cpp @@ -1,130 +1,115 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #include QmitkAbstractDataStorageModel::QmitkAbstractDataStorageModel(QObject* parent/* = nullptr*/) : QAbstractItemModel(parent) , m_DataStorage(nullptr) , m_NodePredicate(nullptr) { // nothing here } QmitkAbstractDataStorageModel::QmitkAbstractDataStorageModel(mitk::DataStorage* dataStorage, QObject* parent/* = nullptr*/) : QAbstractItemModel(parent) , m_DataStorage(nullptr) , m_NodePredicate(nullptr) { SetDataStorage(dataStorage); } QmitkAbstractDataStorageModel::~QmitkAbstractDataStorageModel() { - if (!m_DataStorage.IsExpired()) + auto dataStorage = m_DataStorage.Lock(); + + if (dataStorage.IsNotNull()) { - auto dataStorage = m_DataStorage.Lock(); - - if (dataStorage.IsNotNull()) - { - // remove Listener for the data storage itself - dataStorage->RemoveObserver(m_DataStorageDeletedTag); - - // remove listener from data storage - dataStorage->AddNodeEvent.RemoveListener( - mitk::MessageDelegate1(this, &QmitkAbstractDataStorageModel::NodeAdded)); - dataStorage->RemoveNodeEvent.RemoveListener( - mitk::MessageDelegate1(this, &QmitkAbstractDataStorageModel::NodeRemoved)); - dataStorage->ChangedNodeEvent.RemoveListener( - mitk::MessageDelegate1(this, &QmitkAbstractDataStorageModel::NodeChanged)); - } + // remove Listener for the data storage itself + dataStorage->RemoveObserver(m_DataStorageDeletedTag); + + // remove listener from data storage + dataStorage->AddNodeEvent.RemoveListener( + mitk::MessageDelegate1(this, &QmitkAbstractDataStorageModel::NodeAdded)); + dataStorage->RemoveNodeEvent.RemoveListener( + mitk::MessageDelegate1(this, &QmitkAbstractDataStorageModel::NodeRemoved)); + dataStorage->ChangedNodeEvent.RemoveListener( + mitk::MessageDelegate1(this, &QmitkAbstractDataStorageModel::NodeChanged)); } } void QmitkAbstractDataStorageModel::SetDataStorage(mitk::DataStorage* dataStorage) { if (m_DataStorage == dataStorage) { return; } - if (!m_DataStorage.IsExpired()) + auto lockedDataStorage = m_DataStorage.Lock(); + + if (lockedDataStorage.IsNotNull()) { - auto dataStorage = m_DataStorage.Lock(); - - if (dataStorage.IsNotNull()) - { - // remove Listener for the data storage itself - dataStorage->RemoveObserver(m_DataStorageDeletedTag); - - // remove listener from old data storage - dataStorage->AddNodeEvent.RemoveListener( - mitk::MessageDelegate1(this, &QmitkAbstractDataStorageModel::NodeAdded)); - dataStorage->RemoveNodeEvent.RemoveListener( - mitk::MessageDelegate1(this, &QmitkAbstractDataStorageModel::NodeRemoved)); - dataStorage->ChangedNodeEvent.RemoveListener( - mitk::MessageDelegate1(this, &QmitkAbstractDataStorageModel::NodeChanged)); - } + // remove Listener for the data storage itself + lockedDataStorage->RemoveObserver(m_DataStorageDeletedTag); + + // remove listener from old data storage + lockedDataStorage->AddNodeEvent.RemoveListener( + mitk::MessageDelegate1(this, &QmitkAbstractDataStorageModel::NodeAdded)); + lockedDataStorage->RemoveNodeEvent.RemoveListener( + mitk::MessageDelegate1(this, &QmitkAbstractDataStorageModel::NodeRemoved)); + lockedDataStorage->ChangedNodeEvent.RemoveListener( + mitk::MessageDelegate1(this, &QmitkAbstractDataStorageModel::NodeChanged)); } m_DataStorage = dataStorage; + lockedDataStorage = m_DataStorage.Lock(); - if (!m_DataStorage.IsExpired()) + if (lockedDataStorage.IsNotNull()) { - auto dataStorage = m_DataStorage.Lock(); - - if (dataStorage.IsNotNull()) - { - // add Listener for the data storage itself - auto command = itk::SimpleMemberCommand::New(); - command->SetCallbackFunction(this, &QmitkAbstractDataStorageModel::SetDataStorageDeleted); - m_DataStorageDeletedTag = dataStorage->AddObserver(itk::DeleteEvent(), command); - - // add listener for new data storage - dataStorage->AddNodeEvent.AddListener( - mitk::MessageDelegate1(this, &QmitkAbstractDataStorageModel::NodeAdded)); - dataStorage->RemoveNodeEvent.AddListener( - mitk::MessageDelegate1(this, &QmitkAbstractDataStorageModel::NodeRemoved)); - dataStorage->ChangedNodeEvent.AddListener( - mitk::MessageDelegate1(this, &QmitkAbstractDataStorageModel::NodeChanged)); - } + // add Listener for the data storage itself + auto command = itk::SimpleMemberCommand::New(); + command->SetCallbackFunction(this, &QmitkAbstractDataStorageModel::SetDataStorageDeleted); + m_DataStorageDeletedTag = lockedDataStorage->AddObserver(itk::DeleteEvent(), command); + + // add listener for new data storage + lockedDataStorage->AddNodeEvent.AddListener( + mitk::MessageDelegate1(this, &QmitkAbstractDataStorageModel::NodeAdded)); + lockedDataStorage->RemoveNodeEvent.AddListener( + mitk::MessageDelegate1(this, &QmitkAbstractDataStorageModel::NodeRemoved)); + lockedDataStorage->ChangedNodeEvent.AddListener( + mitk::MessageDelegate1(this, &QmitkAbstractDataStorageModel::NodeChanged)); } // update model if the data storage has been changed DataStorageChanged(); } -mitk::DataStorage* QmitkAbstractDataStorageModel::GetDataStorage() const +mitk::DataStorage::Pointer QmitkAbstractDataStorageModel::GetDataStorage() const { - if (m_DataStorage.IsExpired()) - { - return nullptr; - } - - return m_DataStorage.Lock().GetPointer(); + return m_DataStorage.Lock(); } void QmitkAbstractDataStorageModel::SetDataStorageDeleted() { this->SetDataStorage(nullptr); } void QmitkAbstractDataStorageModel::SetNodePredicate(const mitk::NodePredicateBase* nodePredicate) { if (m_NodePredicate == nodePredicate) { return; } m_NodePredicate = nodePredicate; // update model if the node predicate has been changed NodePredicateChanged(); } diff --git a/Modules/QtWidgets/src/QmitkDataStorageComboBox.cpp b/Modules/QtWidgets/src/QmitkDataStorageComboBox.cpp index 8620733a7c..6f775ea6c6 100644 --- a/Modules/QtWidgets/src/QmitkDataStorageComboBox.cpp +++ b/Modules/QtWidgets/src/QmitkDataStorageComboBox.cpp @@ -1,467 +1,467 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #include "QmitkDataStorageComboBox.h" #include QmitkDataStorageComboBox::QmitkDataStorageComboBox(QWidget *parent, bool autoSelectNewNodes) : QComboBox(parent), m_DataStorage(nullptr), m_Predicate(nullptr), m_BlockEvents(false), m_AutoSelectNewNodes(autoSelectNewNodes) { this->Init(); } QmitkDataStorageComboBox::QmitkDataStorageComboBox(mitk::DataStorage *dataStorage, const mitk::NodePredicateBase *predicate, QWidget *parent, bool autoSelectNewNodes) : QComboBox(parent), m_DataStorage(nullptr), m_Predicate(predicate), m_BlockEvents(false), m_AutoSelectNewNodes(autoSelectNewNodes) { // make connections, fill combobox this->Init(); this->SetDataStorage(dataStorage); } QmitkDataStorageComboBox::~QmitkDataStorageComboBox() { + auto dataStorage = m_DataStorage.Lock(); + // if there was an old storage, remove listeners - if (!m_DataStorage.IsExpired()) + if (dataStorage.IsNotNull()) { - auto dataStorage = m_DataStorage.Lock(); - dataStorage->AddNodeEvent.RemoveListener( mitk::MessageDelegate1(this, &QmitkDataStorageComboBox::AddNode)); dataStorage->RemoveNodeEvent.RemoveListener( mitk::MessageDelegate1(this, &QmitkDataStorageComboBox::RemoveNode)); } // we have lots of observers to nodes and their name properties, this gets ugly if nodes live longer than the box while (m_Nodes.size() > 0) RemoveNode(0); } int QmitkDataStorageComboBox::Find(const mitk::DataNode *dataNode) const { std::iterator_traits::difference_type index = -1; auto nodeIt = std::find(m_Nodes.begin(), m_Nodes.end(), dataNode); if (nodeIt != m_Nodes.end()) index = std::distance(m_Nodes.begin(), nodeIt); return static_cast(index); } mitk::DataStorage::Pointer QmitkDataStorageComboBox::GetDataStorage() const { return m_DataStorage.Lock(); } const mitk::NodePredicateBase::ConstPointer QmitkDataStorageComboBox::GetPredicate() const { return m_Predicate.GetPointer(); } mitk::DataNode::Pointer QmitkDataStorageComboBox::GetNode(int index) const { return (this->HasIndex(index)) ? m_Nodes.at(index) : nullptr; } mitk::DataNode::Pointer QmitkDataStorageComboBox::GetSelectedNode() const { if (this->count() == 0) return nullptr; int currentIndex = this->currentIndex(); return currentIndex >= 0 ? this->GetNode(currentIndex) : nullptr; } mitk::DataStorage::SetOfObjects::ConstPointer QmitkDataStorageComboBox::GetNodes() const { mitk::DataStorage::SetOfObjects::Pointer setOfObjects = mitk::DataStorage::SetOfObjects::New(); for (auto it = m_Nodes.begin(); it != m_Nodes.end(); ++it) { setOfObjects->push_back(*it); } return setOfObjects.GetPointer(); } bool QmitkDataStorageComboBox::GetAutoSelectNewItems() { return m_AutoSelectNewNodes; } void QmitkDataStorageComboBox::SetDataStorage(mitk::DataStorage *dataStorage) { auto currentDataStorage = m_DataStorage.Lock(); // reset only if datastorage really changed if (currentDataStorage.GetPointer() != dataStorage) { // if there was an old storage, remove listeners if (currentDataStorage.IsNotNull()) { currentDataStorage->AddNodeEvent.RemoveListener( mitk::MessageDelegate1(this, &QmitkDataStorageComboBox::AddNode)); currentDataStorage->RemoveNodeEvent.RemoveListener( mitk::MessageDelegate1(this, &QmitkDataStorageComboBox::RemoveNode)); } // set new storage m_DataStorage = dataStorage; + currentDataStorage = m_DataStorage.Lock(); // if there is a new storage, add listeners - if (!m_DataStorage.IsExpired()) + if (currentDataStorage.IsNotNull()) { - currentDataStorage = m_DataStorage.Lock(); - currentDataStorage->AddNodeEvent.AddListener( mitk::MessageDelegate1(this, &QmitkDataStorageComboBox::AddNode)); currentDataStorage->RemoveNodeEvent.AddListener( mitk::MessageDelegate1(this, &QmitkDataStorageComboBox::RemoveNode)); } // reset predicate to reset the combobox this->Reset(); } } void QmitkDataStorageComboBox::SetPredicate(const mitk::NodePredicateBase *predicate) { if (m_Predicate != predicate) { m_Predicate = predicate; this->Reset(); } } void QmitkDataStorageComboBox::AddNode(const mitk::DataNode *dataNode) { // this is an event function, make sure that we didn't call ourself if (!m_BlockEvents) { m_BlockEvents = true; // pass a -1 to the InsertNode function in order to append the datatree node to the end this->InsertNode(-1, dataNode); m_BlockEvents = false; } } void QmitkDataStorageComboBox::RemoveNode(int index) { if (this->HasIndex(index)) { RemoveNodeAndPropertyLists(index); // remove node name from combobox this->removeItem(index); } } void QmitkDataStorageComboBox::RemoveNode(const mitk::DataNode *dataNode) { // this is an event function, make sure that we didn't call ourself if (!m_BlockEvents) { m_BlockEvents = true; this->RemoveNode(this->Find(dataNode)); m_BlockEvents = false; } } void QmitkDataStorageComboBox::SetNode(int index, const mitk::DataNode *dataNode) { if (this->HasIndex(index)) { // if node is identical, we only update the name in the QComboBoxItem if (dataNode == m_Nodes.at(index)) { this->setItemText(index, QString::fromStdString(dataNode->GetName())); } else { this->InsertNode(index, dataNode); } } } void QmitkDataStorageComboBox::SetNode(const mitk::DataNode *dataNode, const mitk::DataNode *otherDataNode) { this->SetNode(this->Find(dataNode), otherDataNode); } void QmitkDataStorageComboBox::SetAutoSelectNewItems(bool autoSelectNewItems) { m_AutoSelectNewNodes = autoSelectNewItems; } void QmitkDataStorageComboBox::OnPropertyListChanged(const itk::Object *caller, const itk::EventObject &event) { if (!m_BlockEvents) { m_BlockEvents = true; // check if we have a modified event const itk::ModifiedEvent *modifiedEvent = dynamic_cast(&event); if (modifiedEvent) { const mitk::PropertyList *propertyList = dynamic_cast(caller); UpdateComboBoxText(propertyList); } m_BlockEvents = false; } } bool QmitkDataStorageComboBox::HasIndex(unsigned int index) const { return (m_Nodes.size() > 0 && index < m_Nodes.size()); } void QmitkDataStorageComboBox::OnCurrentIndexChanged(int index) { if (index >= 0 && index < this->count()) emit OnSelectionChanged(this->GetSelectedNode()); if (index == -1) emit OnSelectionChanged(nullptr); } void QmitkDataStorageComboBox::SetSelectedNode(const mitk::DataNode::Pointer& node) { int index = this->Find(node); if (index == -1) { MITK_INFO << "QmitkDataStorageComboBox: item not available"; } else { this->setCurrentIndex(index); } } void QmitkDataStorageComboBox::InsertNode(int index, const mitk::DataNode *dataNode) { // check new or updated node first if (m_Predicate.IsNotNull() && !m_Predicate->CheckNode(dataNode)) return; bool addNewNode = false; bool insertNewNode = false; bool changedNode = false; // if this->HasIndex(index), then a node shall be updated if (this->HasIndex(index)) { // if we really have another node at this position then ... if (dataNode != m_Nodes.at(index)) { // ... remove node, then proceed as usual this->RemoveNode(index); insertNewNode = true; } else changedNode = true; } // otherwise a new node shall be added, let index point to the element after the last element else { index = m_Nodes.size(); addNewNode = true; } // const cast because we need non const nodes mitk::DataNode *nonConstDataNode = const_cast(dataNode); if (!changedNode) { // break on duplicated nodes (that doesn't make sense to have duplicates in the combobox) if (this->Find(dataNode) != -1) return; // add modified observer itk::MemberCommand::Pointer propertyListChangedCommand = itk::MemberCommand::New(); propertyListChangedCommand->SetCallbackFunction(this, &QmitkDataStorageComboBox::OnPropertyListChanged); // add observer for the data node property list mitk::PropertyList* dataNodePropertyList = nonConstDataNode->GetPropertyList(); if (nullptr != dataNodePropertyList) { m_DataNodePropertyListObserverTags.push_back(dataNodePropertyList->AddObserver(itk::ModifiedEvent(), propertyListChangedCommand)); } else { // fill vector with invalid value m_DataNodePropertyListObserverTags.push_back(-1); } mitk::PropertyList* baseDataPropertyList; //add observer for the base data property list mitk::BaseData* baseData = dynamic_cast(nonConstDataNode->GetData()); if (nullptr != baseData) { baseDataPropertyList = baseData->GetPropertyList(); if (nullptr != baseDataPropertyList) { m_BaseDatapropertyListObserverTags.push_back(baseDataPropertyList->AddObserver(itk::ModifiedEvent(), propertyListChangedCommand)); } else { // fill vector with invalid value m_BaseDatapropertyListObserverTags.push_back(-1); } } else { // fill vector with invalid value m_BaseDatapropertyListObserverTags.push_back(-1); } } // add node to the vector if (addNewNode) m_Nodes.push_back(nonConstDataNode); else if (insertNewNode) m_Nodes.insert(m_Nodes.begin() + index, nonConstDataNode); if (addNewNode) { this->addItem(QString::fromStdString(nonConstDataNode->GetName())); // select new node if m_AutoSelectNewNodes is true or if we have just added the first node if (m_AutoSelectNewNodes || m_Nodes.size() == 1) this->setCurrentIndex(index); } else { // update text in combobox this->setItemText(index, QString::fromStdString(nonConstDataNode->GetName())); } } void QmitkDataStorageComboBox::Init() { connect(this, SIGNAL(currentIndexChanged(int)), this, SLOT(OnCurrentIndexChanged(int))); } void QmitkDataStorageComboBox::Reset() { // remove all nodes first while (!m_Nodes.empty()) { // remove last node // explicitly calling RemoveNode of QmitkDataStorageComboBox since derived classes may prevent the removal of all // nodes in their respective RemoveNode implementation. This is happening for example in // QmitkDataStorageComboBoxWithSelectNone. QmitkDataStorageComboBox::RemoveNode(m_Nodes.size() - 1); } // clear combobox this->clear(); - if (!m_DataStorage.IsExpired()) + auto dataStorage = m_DataStorage.Lock(); + + if (dataStorage.IsNotNull()) { - auto dataStorage = m_DataStorage.Lock(); mitk::DataStorage::SetOfObjects::ConstPointer setOfObjects; // select all if predicate == nullptr if (m_Predicate.IsNotNull()) setOfObjects = dataStorage->GetSubset(m_Predicate); else setOfObjects = dataStorage->GetAll(); // add all found nodes for (mitk::DataStorage::SetOfObjects::ConstIterator nodeIt = setOfObjects->Begin(); nodeIt != setOfObjects->End(); ++nodeIt) // for each dataNode { // add node to the node vector and to the combobox this->AddNode(nodeIt.Value().GetPointer()); } } } void QmitkDataStorageComboBox::RemoveNodeAndPropertyLists(int index) { // remove itk::Event observer mitk::DataNode *dataNode = m_Nodes.at(index); // remove observer from data node property list mitk::PropertyList* dataNodePropertyList = dataNode->GetPropertyList(); if (nullptr != dataNodePropertyList) { dataNodePropertyList->RemoveObserver(m_DataNodePropertyListObserverTags[index]); // remove observer tags from lists m_DataNodePropertyListObserverTags.erase(m_DataNodePropertyListObserverTags.begin() + index); } // remove observer from base data property list mitk::BaseData* baseData = dynamic_cast(dataNode->GetData()); if (nullptr != baseData) { mitk::PropertyList* baseDataPropertyList = baseData->GetPropertyList(); if (nullptr != dataNodePropertyList) { baseDataPropertyList->RemoveObserver(m_BaseDatapropertyListObserverTags[index]); // remove observer tags from lists m_BaseDatapropertyListObserverTags.erase(m_BaseDatapropertyListObserverTags.begin() + index); } } // remove node from node vector m_Nodes.erase(m_Nodes.begin() + index); } void QmitkDataStorageComboBox::UpdateComboBoxText(const mitk::PropertyList* propertyList) { mitk::PropertyList* dataNodePropertyList = nullptr; mitk::PropertyList* baseDataPropertyList = nullptr; mitk::BaseData* baseData; for (const auto& node : m_Nodes) { dataNodePropertyList = node->GetPropertyList(); baseData = dynamic_cast(node->GetData()); if (nullptr != baseData) { baseDataPropertyList = baseData->GetPropertyList(); } if (propertyList == dataNodePropertyList || propertyList == baseDataPropertyList) { // if one of the property list is the one that has just been modified // get the node's index and set its text to the node name // the node name might have been changed, depending on the modified property list auto index = Find(node); // update text in combobox this->setItemText(index, QString::fromStdString(node->GetName())); return; } } } diff --git a/Modules/QtWidgets/src/QmitkDataStorageDefaultListModel.cpp b/Modules/QtWidgets/src/QmitkDataStorageDefaultListModel.cpp index dd134255e6..5613a76721 100644 --- a/Modules/QtWidgets/src/QmitkDataStorageDefaultListModel.cpp +++ b/Modules/QtWidgets/src/QmitkDataStorageDefaultListModel.cpp @@ -1,179 +1,175 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #include // qt widgets module #include "QmitkCustomVariants.h" #include "QmitkEnums.h" #include "QmitkNodeDescriptorManager.h" QmitkDataStorageDefaultListModel::QmitkDataStorageDefaultListModel(QObject *parent) : QmitkAbstractDataStorageModel(parent) { } void QmitkDataStorageDefaultListModel::DataStorageChanged() { UpdateModelData(); } void QmitkDataStorageDefaultListModel::NodePredicateChanged() { UpdateModelData(); } void QmitkDataStorageDefaultListModel::NodeAdded(const mitk::DataNode* /*node*/) { UpdateModelData(); } void QmitkDataStorageDefaultListModel::NodeChanged(const mitk::DataNode* node) { // since the "NodeChanged" event is sent quite often, we check here, if it is relevant for this model if (m_NodePredicate.IsNull() || m_NodePredicate->CheckNode(node)) { UpdateModelData(); return; } // not relevant - need to check if we have to remove it if (std::find(m_DataNodes.begin(), m_DataNodes.end(), node) != m_DataNodes.end()) { UpdateModelData(); } } void QmitkDataStorageDefaultListModel::NodeRemoved(const mitk::DataNode* /*node*/) { UpdateModelData(); } QModelIndex QmitkDataStorageDefaultListModel::index(int row, int column, const QModelIndex &parent) const { bool hasIndex = this->hasIndex(row, column, parent); if (hasIndex) { return this->createIndex(row, column); } return QModelIndex(); } QModelIndex QmitkDataStorageDefaultListModel::parent(const QModelIndex &/*child*/) const { return QModelIndex(); } int QmitkDataStorageDefaultListModel::rowCount(const QModelIndex &parent) const { if (parent.isValid()) { return 0; } return m_DataNodes.size(); } int QmitkDataStorageDefaultListModel::columnCount(const QModelIndex &parent) const { if (parent.isValid()) { return 0; } return 1; } QVariant QmitkDataStorageDefaultListModel::data(const QModelIndex &index, int role) const { if (!index.isValid() || index.model() != this) { return QVariant(); } if(index.row() < 0 || index.row() >= static_cast(m_DataNodes.size())) { return QVariant(); } mitk::DataNode::Pointer dataNode = m_DataNodes.at(index.row()); QString nodeName = QString::fromStdString(dataNode->GetName()); if (nodeName.isEmpty()) nodeName = "unnamed"; if (role == Qt::DisplayRole) return nodeName; else if (role == Qt::ToolTipRole) return nodeName; else if (role == Qt::DecorationRole) { QmitkNodeDescriptor *nodeDescriptor = QmitkNodeDescriptorManager::GetInstance()->GetDescriptor(dataNode); return nodeDescriptor->GetIcon(dataNode); } else if (role == QmitkDataNodeRole) { return QVariant::fromValue(mitk::DataNode::Pointer(dataNode)); } else if (role == QmitkDataNodeRawPointerRole) { return QVariant::fromValue(dataNode); } return QVariant(); } QVariant QmitkDataStorageDefaultListModel::headerData(int /*section*/, Qt::Orientation /*orientation*/, int /*role*/) const { return QVariant(tr("Nodes")); } Qt::ItemFlags QmitkDataStorageDefaultListModel::flags(const QModelIndex &index) const { if (index.isValid() && index.model() == this) { return Qt::ItemIsEnabled | Qt::ItemIsSelectable; } return Qt::NoItemFlags; } void QmitkDataStorageDefaultListModel::UpdateModelData() { mitk::DataStorage::SetOfObjects::ConstPointer dataNodes; - if (!m_DataStorage.IsExpired()) + auto dataStorage = m_DataStorage.Lock(); + + if (dataStorage.IsNotNull()) { - auto dataStorage = m_DataStorage.Lock(); - if (dataStorage.IsNotNull() && m_NodePredicate.IsNotNull()) - { - dataNodes = dataStorage->GetSubset(m_NodePredicate); - } - else - { - dataNodes = dataStorage->GetAll(); - } + dataNodes = m_NodePredicate.IsNotNull() + ? dataStorage->GetSubset(m_NodePredicate) + : dataStorage->GetAll(); } // update the model, so that it will be filled with the nodes of the new data storage beginResetModel(); m_DataNodes.clear(); // add all (filtered) nodes to the vector of nodes if (dataNodes != nullptr) { for (auto& node : *dataNodes) { m_DataNodes.push_back(node); } } endResetModel(); } diff --git a/Modules/QtWidgets/src/QmitkDataStorageHistoryModel.cpp b/Modules/QtWidgets/src/QmitkDataStorageHistoryModel.cpp index c394e7cf83..a28ccce3d3 100644 --- a/Modules/QtWidgets/src/QmitkDataStorageHistoryModel.cpp +++ b/Modules/QtWidgets/src/QmitkDataStorageHistoryModel.cpp @@ -1,89 +1,80 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #include #include "mitkWeakPointer.h" #include #include #include /** Type of the internal history container. Remark: It stores raw pointer instead of mitk::WeakPointer because this lead to occasional crashes when the application was closed (see T24770 for a similar problem with the same reason*/ using NodeHistoryType = std::deque< const mitk::DataNode* >; /** History of node selection. It is sorted from new to old.*/ NodeHistoryType _nodeHistory; std::mutex _historyMutex; QmitkDataStorageHistoryModel::QmitkDataStorageHistoryModel(QObject *parent) : QmitkDataStorageDefaultListModel(parent) { } void QmitkDataStorageHistoryModel::UpdateModelData() { std::vector dataNodes; - if (!m_DataStorage.IsExpired()) + auto dataStorage = m_DataStorage.Lock(); + if (dataStorage.IsNotNull()) { - auto dataStorage = m_DataStorage.Lock(); - if (dataStorage.IsNotNull()) + auto nodesCandidats = m_NodePredicate.IsNotNull() + ? dataStorage->GetSubset(m_NodePredicate) + : dataStorage->GetAll(); + + const std::lock_guard lock(_historyMutex); + + for (auto historyNode : _nodeHistory) + { + auto finding = std::find(nodesCandidats->begin(), nodesCandidats->end(), historyNode); + if (finding != nodesCandidats->end()) { - mitk::DataStorage::SetOfObjects::ConstPointer nodesCandidats; - if (m_NodePredicate.IsNotNull()) - { - nodesCandidats = dataStorage->GetSubset(m_NodePredicate); - } - else - { - nodesCandidats = dataStorage->GetAll(); - } - - const std::lock_guard lock(_historyMutex); - - for (auto historyNode : _nodeHistory) - { - auto finding = std::find(nodesCandidats->begin(), nodesCandidats->end(), historyNode); - if (finding != nodesCandidats->end()) - { - dataNodes.push_back(*finding); - } - } + dataNodes.push_back(*finding); } + } } // update the model, so that it will be filled with the nodes of the new data storage beginResetModel(); m_DataNodes = dataNodes; endResetModel(); } void QmitkDataStorageHistoryModel::AddNodeToHistory(mitk::DataNode* node) { const std::lock_guard lock(_historyMutex); auto finding = std::find(std::begin(_nodeHistory), std::end(_nodeHistory), node); while (finding != std::end(_nodeHistory)) { _nodeHistory.erase(finding); finding = std::find(std::begin(_nodeHistory), std::end(_nodeHistory), node); } _nodeHistory.push_front(node); } void QmitkDataStorageHistoryModel::ResetHistory() { const std::lock_guard lock(_historyMutex); _nodeHistory.clear(); } diff --git a/Modules/QtWidgets/src/QmitkDataStorageSimpleTreeModel.cpp b/Modules/QtWidgets/src/QmitkDataStorageSimpleTreeModel.cpp index b85cb7e2bd..5cc4153ab6 100644 --- a/Modules/QtWidgets/src/QmitkDataStorageSimpleTreeModel.cpp +++ b/Modules/QtWidgets/src/QmitkDataStorageSimpleTreeModel.cpp @@ -1,371 +1,375 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #include #include #include // qt widgets module #include "QmitkCustomVariants.h" #include "QmitkEnums.h" QmitkDataStorageSimpleTreeModel::QmitkDataStorageSimpleTreeModel(QObject *parent) : QmitkAbstractDataStorageModel(parent) , m_Root(nullptr) { ResetTree(); } QmitkDataStorageSimpleTreeModel::~QmitkDataStorageSimpleTreeModel() { m_Root->Delete(); m_Root = nullptr; } void QmitkDataStorageSimpleTreeModel::ResetTree() { mitk::DataNode::Pointer rootDataNode = mitk::DataNode::New(); rootDataNode->SetName("Data Storage"); m_Root = new TreeItem(rootDataNode, nullptr); } void QmitkDataStorageSimpleTreeModel::DataStorageChanged() { if (m_Root) { m_Root->Delete(); } beginResetModel(); ResetTree(); UpdateModelData(); endResetModel(); } void QmitkDataStorageSimpleTreeModel::NodePredicateChanged() { beginResetModel(); ResetTree(); UpdateModelData(); endResetModel(); } void QmitkDataStorageSimpleTreeModel::NodeAdded(const mitk::DataNode *node) { - if (node == nullptr || m_DataStorage.IsExpired() || !m_DataStorage.Lock()->Exists(node) || + auto dataStorage = m_DataStorage.Lock(); + + if (node == nullptr || dataStorage.IsNull() || !dataStorage->Exists(node) || m_Root->Find(node) != nullptr) return; this->AddNodeInternal(node); } void QmitkDataStorageSimpleTreeModel::NodeChanged(const mitk::DataNode *node) { TreeItem *treeItem = m_Root->Find(node); if (treeItem) { TreeItem *parentTreeItem = treeItem->GetParent(); // as the root node should not be removed one should always have a parent item if (!parentTreeItem) return; QModelIndex index = this->createIndex(treeItem->GetIndex(), 0, treeItem); // now emit the dataChanged signal emit dataChanged(index, index); } } void QmitkDataStorageSimpleTreeModel::NodeRemoved(const mitk::DataNode *node) { if (node == nullptr || !m_Root) return; TreeItem *treeItem = m_Root->Find(node); if (!treeItem) return; // return because there is no treeitem containing this node TreeItem *parentTreeItem = treeItem->GetParent(); QModelIndex parentIndex = this->IndexFromTreeItem(parentTreeItem); // emit beginRemoveRows event (QModelIndex is empty because we dont have a tree model) this->beginRemoveRows(parentIndex, treeItem->GetIndex(), treeItem->GetIndex()); // remove node std::vector children = treeItem->GetChildren(); m_TreeItems.remove(treeItem); delete treeItem; //delete in tree if (!children.empty()) { //if not empty we have to rebuild the whole representation, //because the children could be now top level, or at another //source/parent. this->UpdateModelData(); } } QModelIndex QmitkDataStorageSimpleTreeModel::index(int row, int column, const QModelIndex &parent) const { TreeItem *parentItem; if (!parent.isValid() || parent.model() != this) parentItem = m_Root; else parentItem = static_cast(parent.internalPointer()); if (parentItem) { TreeItem *childItem = parentItem->GetChild(row); if (childItem) return createIndex(row, column, childItem); } return QModelIndex(); } QModelIndex QmitkDataStorageSimpleTreeModel::parent(const QModelIndex &child) const { if (!child.isValid() || !m_Root || child.model() != this) return QModelIndex(); TreeItem *childItem = this->TreeItemFromIndex(child); if (!childItem) return QModelIndex(); TreeItem *parentItem = childItem->GetParent(); if (parentItem == m_Root) return QModelIndex(); return this->createIndex(parentItem->GetIndex(), 0, parentItem); } QmitkDataStorageSimpleTreeModel::TreeItem *QmitkDataStorageSimpleTreeModel::TreeItemFromIndex( const QModelIndex &index) const { if (index.isValid() && index.model() == this) { auto item = static_cast(index.internalPointer()); auto finding = std::find(std::begin(m_TreeItems), std::end(m_TreeItems), item); if (finding == std::end(m_TreeItems)) { return nullptr; } return item; } else return m_Root; } int QmitkDataStorageSimpleTreeModel::rowCount(const QModelIndex &parent) const { TreeItem *parentTreeItem = this->TreeItemFromIndex(parent); if (parentTreeItem) return parentTreeItem->GetChildCount(); else return 0; } int QmitkDataStorageSimpleTreeModel::columnCount(const QModelIndex &/*parent*/) const { return 1; } QVariant QmitkDataStorageSimpleTreeModel::data(const QModelIndex &index, int role) const { if (!index.isValid() || index.model() != this) { return QVariant(); } auto treeItem = this->TreeItemFromIndex(index); if (!treeItem) return QVariant(); mitk::DataNode *dataNode = treeItem->GetDataNode(); QString nodeName = QString::fromStdString(dataNode->GetName()); if (nodeName.isEmpty()) { nodeName = "unnamed"; } if (role == Qt::DisplayRole) return nodeName; else if (role == Qt::ToolTipRole) return nodeName; else if (role == Qt::DecorationRole) { QmitkNodeDescriptor *nodeDescriptor = QmitkNodeDescriptorManager::GetInstance()->GetDescriptor(dataNode); return nodeDescriptor->GetIcon(dataNode); } else if (role == QmitkDataNodeRole) { return QVariant::fromValue(mitk::DataNode::Pointer(dataNode)); } else if (role == QmitkDataNodeRawPointerRole) { return QVariant::fromValue(dataNode); } return QVariant(); } bool QmitkDataStorageSimpleTreeModel::setData(const QModelIndex &index, const QVariant &value, int role) { if (!index.isValid() || index.model() != this) return false; auto treeItem = this->TreeItemFromIndex(index); if (!treeItem) return false; mitk::DataNode *dataNode = treeItem->GetDataNode(); if (!dataNode) return false; if (role == Qt::EditRole && !value.toString().isEmpty()) { dataNode->SetName(value.toString().toStdString().c_str()); } else if (role == Qt::CheckStateRole) { // Please note: value.toInt() returns 2, independentely from the actual checkstate of the index element. // Therefore the checkstate is being estimated again here. QVariant qcheckstate = index.data(Qt::CheckStateRole); int checkstate = qcheckstate.toInt(); bool isVisible = bool(checkstate); dataNode->SetVisibility(!isVisible); } // inform listeners about changes emit dataChanged(index, index); return true; } QVariant QmitkDataStorageSimpleTreeModel::headerData(int /*section*/, Qt::Orientation orientation, int role) const { if (orientation == Qt::Horizontal && role == Qt::DisplayRole && m_Root) return QString::fromStdString(m_Root->GetDataNode()->GetName()); return QVariant(); } Qt::ItemFlags QmitkDataStorageSimpleTreeModel::flags(const QModelIndex &index) const { if (index.isValid() && index.model() == this) { auto treeItem = this->TreeItemFromIndex(index); if (!treeItem) return Qt::NoItemFlags; const auto dataNode = treeItem->GetDataNode(); if (m_NodePredicate.IsNull() || m_NodePredicate->CheckNode(dataNode)) { return Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsEditable; } else { return Qt::NoItemFlags; } } return Qt::NoItemFlags; } mitk::DataNode *QmitkDataStorageSimpleTreeModel::GetParentNode(const mitk::DataNode *node) const { mitk::DataNode *dataNode = nullptr; mitk::DataStorage::SetOfObjects::ConstPointer _Sources = m_DataStorage.Lock()->GetSources(node); if (_Sources->Size() > 0) dataNode = _Sources->front(); return dataNode; } void QmitkDataStorageSimpleTreeModel::AddNodeInternal(const mitk::DataNode *node) { - if (node == nullptr || m_DataStorage.IsExpired() || !m_DataStorage.Lock()->Exists(node) || m_Root->Find(node) != nullptr) + auto dataStorage = m_DataStorage.Lock(); + + if (node == nullptr || dataStorage.IsNull() || !dataStorage->Exists(node) || m_Root->Find(node) != nullptr) return; // find out if we have a root node TreeItem *parentTreeItem = m_Root; QModelIndex index; mitk::DataNode *parentDataNode = this->GetParentNode(node); if (parentDataNode) // no top level data node { parentTreeItem = m_Root->Find(parentDataNode); // find the corresponding tree item if (!parentTreeItem) { this->NodeAdded(parentDataNode); parentTreeItem = m_Root->Find(parentDataNode); if (!parentTreeItem) return; } // get the index of this parent with the help of the grand parent index = this->createIndex(parentTreeItem->GetIndex(), 0, parentTreeItem); } int firstRowWithASiblingBelow = 0; int nodeLayer = -1; node->GetIntProperty("layer", nodeLayer); for (TreeItem *siblingTreeItem : parentTreeItem->GetChildren()) { int siblingLayer = -1; if (mitk::DataNode *siblingNode = siblingTreeItem->GetDataNode()) { siblingNode->GetIntProperty("layer", siblingLayer); } if (nodeLayer > siblingLayer) { break; } ++firstRowWithASiblingBelow; } beginInsertRows(index, firstRowWithASiblingBelow, firstRowWithASiblingBelow); auto newNode = new TreeItem(const_cast(node)); parentTreeItem->InsertChild(newNode, firstRowWithASiblingBelow); m_TreeItems.push_back(newNode); endInsertRows(); } QModelIndex QmitkDataStorageSimpleTreeModel::IndexFromTreeItem(TreeItem *item) const { if (item == m_Root) return QModelIndex(); else return this->createIndex(item->GetIndex(), 0, item); } void QmitkDataStorageSimpleTreeModel::UpdateModelData() { - if (!m_DataStorage.IsExpired()) + auto dataStorage = m_DataStorage.Lock(); + + if (dataStorage.IsNotNull()) { - auto nodeset = m_DataStorage.Lock()->GetAll(); - if (m_NodePredicate != nullptr) - { - nodeset = m_DataStorage.Lock()->GetSubset(m_NodePredicate); - } + auto nodeset = m_NodePredicate != nullptr + ? dataStorage->GetSubset(m_NodePredicate) + : dataStorage->GetAll(); for (const auto& node : *nodeset) { this->AddNodeInternal(node); } } } diff --git a/Modules/QtWidgets/src/QmitkDataStorageTableModel.cpp b/Modules/QtWidgets/src/QmitkDataStorageTableModel.cpp index 96884c3dff..a877e2f8ee 100644 --- a/Modules/QtWidgets/src/QmitkDataStorageTableModel.cpp +++ b/Modules/QtWidgets/src/QmitkDataStorageTableModel.cpp @@ -1,516 +1,509 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #include "QmitkDataStorageTableModel.h" //# Own includes #include "QmitkCustomVariants.h" #include "QmitkEnums.h" #include "mitkNodePredicateBase.h" #include "mitkProperties.h" #include "mitkRenderingManager.h" #include //# Toolkit includes #include #include #include //#CTORS/DTOR QmitkDataStorageTableModel::QmitkDataStorageTableModel(mitk::DataStorage::Pointer _DataStorage, mitk::NodePredicateBase *_Predicate, QObject *parent) : QAbstractTableModel(parent), m_DataStorage(nullptr), m_Predicate(nullptr), m_BlockEvents(false), m_SortDescending(false) { this->SetPredicate(_Predicate); this->SetDataStorage(_DataStorage); } QmitkDataStorageTableModel::~QmitkDataStorageTableModel() { // set data storage 0 to remove event listeners this->SetDataStorage(nullptr); } //# Public GETTER const mitk::DataStorage::Pointer QmitkDataStorageTableModel::GetDataStorage() const { return m_DataStorage.Lock(); } mitk::NodePredicateBase::Pointer QmitkDataStorageTableModel::GetPredicate() const { return m_Predicate; } mitk::DataNode::Pointer QmitkDataStorageTableModel::GetNode(const QModelIndex &index) const { mitk::DataNode::Pointer node; if (index.isValid()) { node = m_NodeSet.at(index.row()); } return node; } QVariant QmitkDataStorageTableModel::headerData(int section, Qt::Orientation orientation, int role) const { QVariant headerData; // show only horizontal header if (role == Qt::DisplayRole) { if (orientation == Qt::Horizontal) { // first column: "Name" if (section == 0) headerData = "Name"; else if (section == 1) headerData = "Data Type"; else if (section == 2) headerData = "Visibility"; } else if (orientation == Qt::Vertical) { // show numbers for rows headerData = section + 1; } } return headerData; } Qt::ItemFlags QmitkDataStorageTableModel::flags(const QModelIndex &index) const { Qt::ItemFlags flags = QAbstractItemModel::flags(index); // name & visibility is editable if (index.column() == 0) { flags |= Qt::ItemIsEditable; } else if (index.column() == 2) { flags |= Qt::ItemIsUserCheckable; } return flags; } int QmitkDataStorageTableModel::rowCount(const QModelIndex &) const { return m_NodeSet.size(); } int QmitkDataStorageTableModel::columnCount(const QModelIndex &) const { // show name, type and visible columnn int columns = 3; return columns; } QVariant QmitkDataStorageTableModel::data(const QModelIndex &index, int role) const { QVariant data; if (index.isValid() && !m_NodeSet.empty()) { mitk::DataNode::Pointer node = m_NodeSet.at(index.row()); std::string nodeName = node->GetName(); if (nodeName.empty()) nodeName = "unnamed"; // get name if (index.column() == 0) { // get name of node (may also be edited) if (role == Qt::DisplayRole || role == Qt::EditRole) { data = QString::fromStdString(nodeName); } else if (role == QmitkDataNodeRole) { data = QVariant::fromValue(node); } } else if (index.column() == 1) { QmitkNodeDescriptor *nodeDescriptor = QmitkNodeDescriptorManager::GetInstance()->GetDescriptor(node); // get type property of mitk::BaseData if (role == Qt::DisplayRole) { data = nodeDescriptor->GetNameOfClass(); } // show some nice icons for datatype else if (role == Qt::DecorationRole) { data = nodeDescriptor->GetIcon(node); } } else if (index.column() == 2) { // get visible property of mitk::BaseData bool visibility = false; if (node->GetVisibility(visibility, nullptr) && role == Qt::CheckStateRole) { data = (visibility ? Qt::Checked : Qt::Unchecked); } // node->GetVisibility(visibility, 0) && role == Qt::CheckStateRole } // index.column() == 2 } // index.isValid() && !m_NodeSet.empty() return data; } //# Public SETTERS void QmitkDataStorageTableModel::SetPredicate(mitk::NodePredicateBase *_Predicate) { // ensure that a new predicate is set in order to avoid unnecessary changed events if (m_Predicate != _Predicate) { m_Predicate = _Predicate; this->Reset(); } } void QmitkDataStorageTableModel::SetDataStorage(mitk::DataStorage::Pointer _DataStorage) { // only proceed if we have a new datastorage if (m_DataStorage != _DataStorage) { + auto dataStorage = m_DataStorage.Lock(); + // if a data storage was set before remove old event listeners - if (!m_DataStorage.IsExpired()) + if (dataStorage.IsNotNull()) { - auto dataStorage = m_DataStorage.Lock(); - dataStorage->AddNodeEvent.RemoveListener( mitk::MessageDelegate1( this, &QmitkDataStorageTableModel::AddNode)); dataStorage->RemoveNodeEvent.RemoveListener( mitk::MessageDelegate1( this, &QmitkDataStorageTableModel::RemoveNode)); } // set new data storage m_DataStorage = _DataStorage; + dataStorage = m_DataStorage.Lock(); // if new storage is not 0 subscribe for events - if (!m_DataStorage.IsExpired()) + if (dataStorage.IsNotNull()) { - auto dataStorage = m_DataStorage.Lock(); - // subscribe for node added/removed events dataStorage->AddNodeEvent.AddListener( mitk::MessageDelegate1( this, &QmitkDataStorageTableModel::AddNode)); dataStorage->RemoveNodeEvent.AddListener( mitk::MessageDelegate1( this, &QmitkDataStorageTableModel::RemoveNode)); } // Reset model (even if datastorage is 0->will be checked in Reset()) this->Reset(); } } void QmitkDataStorageTableModel::AddNode(const mitk::DataNode *node) { // garantuee no recursions when a new node event is thrown if (!m_BlockEvents) { // if we have a predicate, check node against predicate first if (m_Predicate.IsNotNull() && !m_Predicate->CheckNode(node)) return; // dont add nodes without data (formerly known as helper objects) if (node->GetData() == nullptr) return; // create listener commands to listen to changes in the name or the visibility of the node itk::MemberCommand::Pointer propertyModifiedCommand = itk::MemberCommand::New(); propertyModifiedCommand->SetCallbackFunction(this, &QmitkDataStorageTableModel::PropertyModified); mitk::BaseProperty *tempProperty = nullptr; // add listener for properties tempProperty = node->GetProperty("visible"); if (tempProperty) m_VisiblePropertyModifiedObserverTags[tempProperty] = tempProperty->AddObserver(itk::ModifiedEvent(), propertyModifiedCommand); tempProperty = node->GetProperty("name"); if (tempProperty) m_NamePropertyModifiedObserverTags[tempProperty] = tempProperty->AddObserver(itk::ModifiedEvent(), propertyModifiedCommand); // emit beginInsertRows event beginInsertRows(QModelIndex(), m_NodeSet.size(), m_NodeSet.size()); // add node m_NodeSet.push_back(const_cast(node)); // emit endInsertRows event endInsertRows(); } } void QmitkDataStorageTableModel::RemoveNode(const mitk::DataNode *node) { // garantuee no recursions when a new node event is thrown if (!m_BlockEvents) { // find corresponding node auto nodeIt = std::find(m_NodeSet.begin(), m_NodeSet.end(), node); if (nodeIt != m_NodeSet.end()) { // now: remove listeners for name property ... mitk::BaseProperty *tempProperty = nullptr; tempProperty = (*nodeIt)->GetProperty("visible"); if (tempProperty) tempProperty->RemoveObserver(m_VisiblePropertyModifiedObserverTags[tempProperty]); m_VisiblePropertyModifiedObserverTags.erase(tempProperty); // ... and visibility property tempProperty = (*nodeIt)->GetProperty("name"); if (tempProperty) tempProperty->RemoveObserver(m_NamePropertyModifiedObserverTags[tempProperty]); m_NamePropertyModifiedObserverTags.erase(tempProperty); // get an index from iterator int row = std::distance(m_NodeSet.begin(), nodeIt); // emit beginRemoveRows event (QModelIndex is empty because we dont have a tree model) this->beginRemoveRows(QModelIndex(), row, row); // remove node m_NodeSet.erase(nodeIt); // emit endRemoveRows event endRemoveRows(); } } } void QmitkDataStorageTableModel::PropertyModified(const itk::Object *caller, const itk::EventObject &) { if (!m_BlockEvents) { // get modified property const mitk::BaseProperty *modifiedProperty = dynamic_cast(caller); if (modifiedProperty) { // find node that holds the modified property int row = -1; int column = -1; std::vector::iterator it; mitk::BaseProperty *visibilityProperty = nullptr; mitk::BaseProperty *nameProperty = nullptr; // search for property that changed and emit datachanged on the corresponding ModelIndex for (it = m_NodeSet.begin(); it != m_NodeSet.end(); it++) { // check for the visible property or the name property visibilityProperty = (*it)->GetProperty("visible"); if (modifiedProperty == visibilityProperty) { column = 2; break; } nameProperty = (*it)->GetProperty("name"); if (modifiedProperty == nameProperty) { column = 0; break; } } // if we have the property we have a valid iterator if (it != m_NodeSet.end()) row = std::distance(m_NodeSet.begin(), it); // now emit the dataChanged signal QModelIndex indexOfChangedProperty = index(row, column); emit dataChanged(indexOfChangedProperty, indexOfChangedProperty); } } } bool QmitkDataStorageTableModel::setData(const QModelIndex &index, const QVariant &value, int role) { bool noErr = false; if (index.isValid() && (role == Qt::EditRole || role == Qt::CheckStateRole)) { // any change events produced here should not be caught in this class // --> set m_BlockEvents to true m_BlockEvents = true; mitk::DataNode::Pointer node = m_NodeSet.at(index.row()); if (index.column() == 0) { node->SetStringProperty("name", value.toString().toStdString().c_str()); } else if (index.column() == 2) { node->SetBoolProperty("visible", (value.toInt() == Qt::Checked ? true : false)); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } // inform listeners about changes emit dataChanged(index, index); m_BlockEvents = false; noErr = true; } return noErr; } //#Protected SETTER void QmitkDataStorageTableModel::Reset() { mitk::DataStorage::SetOfObjects::ConstPointer _NodeSet; // remove all nodes now (dont use iterators because removing elements // would invalidate the iterator) // start at the last element: first in, last out unsigned int i = m_NodeSet.size(); while (!m_NodeSet.empty()) { --i; this->RemoveNode(m_NodeSet.at(i)); } // normally now everything should be empty->just to be sure // erase all arrays again m_NamePropertyModifiedObserverTags.clear(); m_VisiblePropertyModifiedObserverTags.clear(); m_NodeSet.clear(); + auto dataStorage = m_DataStorage.Lock(); + // the whole reset depends on the fact if a data storage is set or not - if (!m_DataStorage.IsExpired()) + if (dataStorage.IsNotNull()) { - auto dataStorage = m_DataStorage.Lock(); - - if (m_Predicate.IsNotNull()) - // get subset - _NodeSet = dataStorage->GetSubset(m_Predicate); - // if predicate is nullptr, select all nodes - else - { - _NodeSet = dataStorage->GetAll(); - // remove ghost root node - } + _NodeSet = m_Predicate.IsNotNull() + ? dataStorage->GetSubset(m_Predicate) + : dataStorage->GetAll(); // finally add all nodes to the model for (auto it = _NodeSet->begin(); it != _NodeSet->end(); it++) { // save node this->AddNode(*it); } } } void QmitkDataStorageTableModel::sort(int column, Qt::SortOrder order /*= Qt::AscendingOrder */) { bool sortDescending = (order == Qt::DescendingOrder) ? true : false; // do not sort twice !!! (dont know why, but qt calls this func twice. STUPID!) /* if(sortDescending != m_SortDescending) {*/ // m_SortDescending = sortDescending; DataNodeCompareFunction::CompareCriteria _CompareCriteria = DataNodeCompareFunction::CompareByName; DataNodeCompareFunction::CompareOperator _CompareOperator = sortDescending ? DataNodeCompareFunction::Greater : DataNodeCompareFunction::Less; if (column == 1) _CompareCriteria = DataNodeCompareFunction::CompareByClassName; else if (column == 2) _CompareCriteria = DataNodeCompareFunction::CompareByVisibility; DataNodeCompareFunction compareFunc(_CompareCriteria, _CompareOperator); std::sort(m_NodeSet.begin(), m_NodeSet.end(), compareFunc); QAbstractTableModel::beginResetModel(); QAbstractTableModel::endResetModel(); //} } std::vector QmitkDataStorageTableModel::GetNodeSet() const { return m_NodeSet; } QmitkDataStorageTableModel::DataNodeCompareFunction::DataNodeCompareFunction(CompareCriteria _CompareCriteria, CompareOperator _CompareOperator) : m_CompareCriteria(_CompareCriteria), m_CompareOperator(_CompareOperator) { } bool QmitkDataStorageTableModel::DataNodeCompareFunction::operator()(const mitk::DataNode::Pointer &_Left, const mitk::DataNode::Pointer &_Right) const { switch (m_CompareCriteria) { case CompareByClassName: if (m_CompareOperator == Less) return (_Left->GetData()->GetNameOfClass() < _Right->GetData()->GetNameOfClass()); else return (_Left->GetData()->GetNameOfClass() > _Right->GetData()->GetNameOfClass()); break; case CompareByVisibility: { bool _LeftVisibility = false; bool _RightVisibility = false; _Left->GetVisibility(_LeftVisibility, nullptr); _Right->GetVisibility(_RightVisibility, nullptr); if (m_CompareOperator == Less) return (_LeftVisibility < _RightVisibility); else return (_LeftVisibility > _RightVisibility); } break; // CompareByName: default: if (m_CompareOperator == Less) return (_Left->GetName() < _Right->GetName()); else return (_Left->GetName() > _Right->GetName()); break; } } diff --git a/Modules/QtWidgets/src/QmitkDataStorageTreeModel.cpp b/Modules/QtWidgets/src/QmitkDataStorageTreeModel.cpp index 2ab4f4d8be..c9e6644716 100644 --- a/Modules/QtWidgets/src/QmitkDataStorageTreeModel.cpp +++ b/Modules/QtWidgets/src/QmitkDataStorageTreeModel.cpp @@ -1,880 +1,884 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #include #include #include #include #include #include #include #include #include #include #include #include "QmitkDataStorageTreeModel.h" #include "QmitkDataStorageTreeModelInternalItem.h" #include "QmitkNodeDescriptorManager.h" #include #include #include #include #include #include #include #include #include QmitkDataStorageTreeModel::QmitkDataStorageTreeModel(mitk::DataStorage *_DataStorage, bool _PlaceNewNodesOnTop, QObject *parent) : QAbstractItemModel(parent), m_DataStorage(nullptr), m_PlaceNewNodesOnTop(_PlaceNewNodesOnTop), m_Root(nullptr), m_BlockDataStorageEvents(false), m_AllowHierarchyChange(false) { this->SetDataStorage(_DataStorage); } QmitkDataStorageTreeModel::~QmitkDataStorageTreeModel() { // set data storage to 0 = remove all listeners this->SetDataStorage(nullptr); m_Root->Delete(); m_Root = nullptr; } mitk::DataNode::Pointer QmitkDataStorageTreeModel::GetNode(const QModelIndex &index) const { return this->TreeItemFromIndex(index)->GetDataNode(); } const mitk::DataStorage::Pointer QmitkDataStorageTreeModel::GetDataStorage() const { return m_DataStorage.Lock(); } QModelIndex QmitkDataStorageTreeModel::index(int row, int column, const QModelIndex &parent) const { TreeItem *parentItem; if (!parent.isValid()) parentItem = m_Root; else parentItem = static_cast(parent.internalPointer()); TreeItem *childItem = parentItem->GetChild(row); if (childItem) return createIndex(row, column, childItem); else return QModelIndex(); } int QmitkDataStorageTreeModel::rowCount(const QModelIndex &parent) const { TreeItem *parentTreeItem = this->TreeItemFromIndex(parent); return parentTreeItem->GetChildCount(); } Qt::ItemFlags QmitkDataStorageTreeModel::flags(const QModelIndex &index) const { if (index.isValid()) { return Qt::ItemIsUserCheckable | Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsEditable | Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled; } else { return Qt::ItemIsDropEnabled; } } int QmitkDataStorageTreeModel::columnCount(const QModelIndex & /* parent = QModelIndex() */) const { return 1; } QModelIndex QmitkDataStorageTreeModel::parent(const QModelIndex &index) const { if (!index.isValid()) return QModelIndex(); TreeItem *childItem = this->TreeItemFromIndex(index); TreeItem *parentItem = childItem->GetParent(); if (parentItem == m_Root) return QModelIndex(); return this->createIndex(parentItem->GetIndex(), 0, parentItem); } QmitkDataStorageTreeModel::TreeItem *QmitkDataStorageTreeModel::TreeItemFromIndex(const QModelIndex &index) const { if (index.isValid()) return static_cast(index.internalPointer()); else return m_Root; } Qt::DropActions QmitkDataStorageTreeModel::supportedDropActions() const { return Qt::CopyAction | Qt::MoveAction; } Qt::DropActions QmitkDataStorageTreeModel::supportedDragActions() const { return Qt::CopyAction | Qt::MoveAction; } bool QmitkDataStorageTreeModel::dropMimeData( const QMimeData *data, Qt::DropAction action, int row, int /*column*/, const QModelIndex &parent) { // Early exit, returning true, but not actually doing anything (ignoring data). if (action == Qt::IgnoreAction) { return true; } // Note, we are returning true if we handled it, and false otherwise bool returnValue = false; if (data->hasFormat("application/x-qabstractitemmodeldatalist")) { returnValue = true; // First we extract a Qlist of TreeItem* pointers. QList listOfItemsToDrop = ToTreeItemPtrList(data); if (listOfItemsToDrop.empty()) { return false; } // Retrieve the TreeItem* where we are dropping stuff, and its parent. TreeItem *dropItem = this->TreeItemFromIndex(parent); TreeItem *parentItem = dropItem->GetParent(); // If item was dropped onto empty space, we select the root node if (dropItem == m_Root) { parentItem = m_Root; } // Dragging and Dropping is only allowed within the same parent, so use the first item in list to validate. // (otherwise, you could have a derived image such as a segmentation, and assign it to another image). // NOTE: We are assuming the input list is valid... i.e. when it was dragged, all the items had the same parent. // Determine whether or not the drag and drop operation is a valid one. // Examples of invalid operations include: // - dragging nodes with different parents // - dragging nodes from one parent to another parent, if m_AllowHierarchyChange is false // - dragging a node on one of its child nodes (only relevant if m_AllowHierarchyChange is true) bool isValidDragAndDropOperation(true); // different parents { TreeItem *firstParent = listOfItemsToDrop[0]->GetParent(); QList::iterator diIter; for (diIter = listOfItemsToDrop.begin() + 1; diIter != listOfItemsToDrop.end(); diIter++) { if (firstParent != (*diIter)->GetParent()) { isValidDragAndDropOperation = false; break; } } } // dragging from one parent to another if ((!m_AllowHierarchyChange) && isValidDragAndDropOperation) { if (row == -1) // drag onto a node { isValidDragAndDropOperation = listOfItemsToDrop[0]->GetParent() == parentItem; } else // drag between nodes { isValidDragAndDropOperation = listOfItemsToDrop[0]->GetParent() == dropItem; } } // dragging on a child node of one the dragged nodes { QList::iterator diIter; for (diIter = listOfItemsToDrop.begin(); diIter != listOfItemsToDrop.end(); diIter++) { TreeItem *tempItem = dropItem; while (tempItem != m_Root) { tempItem = tempItem->GetParent(); if (tempItem == *diIter) { isValidDragAndDropOperation = false; } } } } if (!isValidDragAndDropOperation) return isValidDragAndDropOperation; if (listOfItemsToDrop[0] != dropItem && isValidDragAndDropOperation) { // Retrieve the index of where we are dropping stuff. QModelIndex parentModelIndex = this->IndexFromTreeItem(parentItem); int dragIndex = 0; // Iterate through the list of TreeItem (which may be at non-consecutive indexes). QList::iterator diIter; for (diIter = listOfItemsToDrop.begin(); diIter != listOfItemsToDrop.end(); diIter++) { TreeItem *itemToDrop = *diIter; // if the item is dragged down we have to compensate its final position for the // fact it is deleted lateron, this only applies if it is dragged within the same level if ((itemToDrop->GetIndex() < row) && (itemToDrop->GetParent() == dropItem)) { dragIndex = 1; } // Here we assume that as you remove items, one at a time, that GetIndex() will be valid. this->beginRemoveRows( this->IndexFromTreeItem(itemToDrop->GetParent()), itemToDrop->GetIndex(), itemToDrop->GetIndex()); itemToDrop->GetParent()->RemoveChild(itemToDrop); this->endRemoveRows(); } // row = -1 dropped on an item, row != -1 dropped in between two items // Select the target index position, or put it at the end of the list. int dropIndex = 0; if (row != -1) { if (dragIndex == 0) dropIndex = std::min(row, parentItem->GetChildCount() - 1); else dropIndex = std::min(row - 1, parentItem->GetChildCount() - 1); } else { dropIndex = dropItem->GetIndex(); } QModelIndex dropItemModelIndex = this->IndexFromTreeItem(dropItem); if ((row == -1 && dropItemModelIndex.row() == -1) || dropItemModelIndex.row() > parentItem->GetChildCount()) dropIndex = parentItem->GetChildCount() - 1; // Now insert items again at the drop item position if (m_AllowHierarchyChange) { this->beginInsertRows(dropItemModelIndex, dropIndex, dropIndex + listOfItemsToDrop.size() - 1); } else { this->beginInsertRows(parentModelIndex, dropIndex, dropIndex + listOfItemsToDrop.size() - 1); } for (diIter = listOfItemsToDrop.begin(); diIter != listOfItemsToDrop.end(); diIter++) { // dropped on node, behaviour depends on preference setting if (m_AllowHierarchyChange) { auto dataStorage = m_DataStorage.Lock(); m_BlockDataStorageEvents = true; mitk::DataNode::Pointer droppedNode = (*diIter)->GetDataNode(); mitk::DataNode *dropOntoNode = dropItem->GetDataNode(); dataStorage->Remove(droppedNode); dataStorage->Add(droppedNode, dropOntoNode); m_BlockDataStorageEvents = false; dropItem->InsertChild((*diIter), dropIndex); } else { if (row == -1) // drag onto a node { parentItem->InsertChild((*diIter), dropIndex); } else // drag between nodes { dropItem->InsertChild((*diIter), dropIndex); } } dropIndex++; } this->endInsertRows(); // Change Layers to match. this->AdjustLayerProperty(); } } else if (data->hasFormat("application/x-mitk-datanodes")) { returnValue = true; int numberOfNodesDropped = 0; QList dataNodeList = QmitkMimeTypes::ToDataNodePtrList(data); mitk::DataNode *node = nullptr; foreach (node, dataNodeList) { auto datastorage = m_DataStorage.Lock(); if (node && datastorage.IsNotNull() && !datastorage->Exists(node)) { m_DataStorage.Lock()->Add(node); mitk::BaseData::Pointer basedata = node->GetData(); if (basedata.IsNotNull()) { mitk::RenderingManager::GetInstance()->InitializeViews(basedata->GetTimeGeometry()); numberOfNodesDropped++; } } } // Only do a rendering update, if we actually dropped anything. if (numberOfNodesDropped > 0) { mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } } return returnValue; } QStringList QmitkDataStorageTreeModel::mimeTypes() const { QStringList types = QAbstractItemModel::mimeTypes(); types << "application/x-qabstractitemmodeldatalist"; types << "application/x-mitk-datanodes"; return types; } QMimeData *QmitkDataStorageTreeModel::mimeData(const QModelIndexList &indexes) const { return mimeDataFromModelIndexList(indexes); } QMimeData *QmitkDataStorageTreeModel::mimeDataFromModelIndexList(const QModelIndexList &indexes) { QMimeData *ret = new QMimeData; QString treeItemAddresses(""); QString dataNodeAddresses(""); QByteArray baTreeItemPtrs; QByteArray baDataNodePtrs; QDataStream dsTreeItemPtrs(&baTreeItemPtrs, QIODevice::WriteOnly); QDataStream dsDataNodePtrs(&baDataNodePtrs, QIODevice::WriteOnly); for (int i = 0; i < indexes.size(); i++) { TreeItem *treeItem = static_cast(indexes.at(i).internalPointer()); dsTreeItemPtrs << reinterpret_cast(treeItem); dsDataNodePtrs << reinterpret_cast(treeItem->GetDataNode().GetPointer()); // --------------- deprecated ----------------- unsigned long long treeItemAddress = reinterpret_cast(treeItem); unsigned long long dataNodeAddress = reinterpret_cast(treeItem->GetDataNode().GetPointer()); QTextStream(&treeItemAddresses) << treeItemAddress; QTextStream(&dataNodeAddresses) << dataNodeAddress; if (i != indexes.size() - 1) { QTextStream(&treeItemAddresses) << ","; QTextStream(&dataNodeAddresses) << ","; } // -------------- end deprecated ------------- } // ------------------ deprecated ----------------- ret->setData("application/x-qabstractitemmodeldatalist", QByteArray(treeItemAddresses.toLatin1())); ret->setData("application/x-mitk-datanodes", QByteArray(dataNodeAddresses.toLatin1())); // --------------- end deprecated ----------------- ret->setData(QmitkMimeTypes::DataStorageTreeItemPtrs, baTreeItemPtrs); ret->setData(QmitkMimeTypes::DataNodePtrs, baDataNodePtrs); return ret; } QVariant QmitkDataStorageTreeModel::data(const QModelIndex &index, int role) const { mitk::DataNode *dataNode = this->TreeItemFromIndex(index)->GetDataNode(); // get name of treeItem (may also be edited) QString nodeName = QString::fromStdString(dataNode->GetName()); if (nodeName.isEmpty()) { nodeName = "unnamed"; } if (role == Qt::DisplayRole) return nodeName; else if (role == Qt::ToolTipRole) return nodeName; else if (role == Qt::DecorationRole) { QmitkNodeDescriptor *nodeDescriptor = QmitkNodeDescriptorManager::GetInstance()->GetDescriptor(dataNode); return nodeDescriptor->GetIcon(dataNode); } else if (role == Qt::CheckStateRole) { return dataNode->IsVisible(nullptr); } else if (role == QmitkDataNodeRole) { return QVariant::fromValue(mitk::DataNode::Pointer(dataNode)); } else if (role == QmitkDataNodeRawPointerRole) { return QVariant::fromValue(dataNode); } return QVariant(); } bool QmitkDataStorageTreeModel::DicomPropertiesExists(const mitk::DataNode &node) const { bool propertiesExists = false; mitk::BaseProperty *seriesDescription_deprecated = (node.GetProperty("dicom.series.SeriesDescription")); mitk::BaseProperty *studyDescription_deprecated = (node.GetProperty("dicom.study.StudyDescription")); mitk::BaseProperty *patientsName_deprecated = (node.GetProperty("dicom.patient.PatientsName")); mitk::BaseProperty *seriesDescription = (node.GetProperty(mitk::GeneratePropertyNameForDICOMTag(0x0008, 0x103e).c_str())); mitk::BaseProperty *studyDescription = (node.GetProperty(mitk::GeneratePropertyNameForDICOMTag(0x0008, 0x1030).c_str())); mitk::BaseProperty *patientsName = (node.GetProperty(mitk::GeneratePropertyNameForDICOMTag(0x0010, 0x0010).c_str())); if (patientsName != nullptr && studyDescription != nullptr && seriesDescription != nullptr) { if ((!patientsName->GetValueAsString().empty()) && (!studyDescription->GetValueAsString().empty()) && (!seriesDescription->GetValueAsString().empty())) { propertiesExists = true; } } /** Code coveres the deprecated property naming for backwards compatibility */ if (patientsName_deprecated != nullptr && studyDescription_deprecated != nullptr && seriesDescription_deprecated != nullptr) { if ((!patientsName_deprecated->GetValueAsString().empty()) && (!studyDescription_deprecated->GetValueAsString().empty()) && (!seriesDescription_deprecated->GetValueAsString().empty())) { propertiesExists = true; } } return propertiesExists; } QVariant QmitkDataStorageTreeModel::headerData(int /*section*/, Qt::Orientation orientation, int role) const { if (orientation == Qt::Horizontal && role == Qt::DisplayRole && m_Root) return QString::fromStdString(m_Root->GetDataNode()->GetName()); return QVariant(); } void QmitkDataStorageTreeModel::SetDataStorage(mitk::DataStorage *_DataStorage) { if (m_DataStorage != _DataStorage) // dont take the same again { auto dataStorage = m_DataStorage.Lock(); if (dataStorage.IsNotNull()) { // remove Listener for the data storage itself dataStorage->RemoveObserver(m_DataStorageDeletedTag); // remove listeners for the nodes dataStorage->AddNodeEvent.RemoveListener( mitk::MessageDelegate1(this, &QmitkDataStorageTreeModel::AddNode)); dataStorage->ChangedNodeEvent.RemoveListener( mitk::MessageDelegate1( this, &QmitkDataStorageTreeModel::SetNodeModified)); dataStorage->RemoveNodeEvent.RemoveListener( mitk::MessageDelegate1( this, &QmitkDataStorageTreeModel::RemoveNode)); } this->beginResetModel(); // take over the new data storage m_DataStorage = _DataStorage; // delete the old root (if necessary, create new) if (m_Root) m_Root->Delete(); mitk::DataNode::Pointer rootDataNode = mitk::DataNode::New(); rootDataNode->SetName("Data Manager"); m_Root = new TreeItem(rootDataNode, nullptr); dataStorage = m_DataStorage.Lock(); if (dataStorage.IsNotNull()) { // add Listener for the data storage itself auto command = itk::SimpleMemberCommand::New(); command->SetCallbackFunction(this, &QmitkDataStorageTreeModel::SetDataStorageDeleted); m_DataStorageDeletedTag = dataStorage->AddObserver(itk::DeleteEvent(), command); // add listeners for the nodes dataStorage->AddNodeEvent.AddListener(mitk::MessageDelegate1( this, &QmitkDataStorageTreeModel::AddNode)); dataStorage->ChangedNodeEvent.AddListener( mitk::MessageDelegate1( this, &QmitkDataStorageTreeModel::SetNodeModified)); dataStorage->RemoveNodeEvent.AddListener( mitk::MessageDelegate1( this, &QmitkDataStorageTreeModel::RemoveNode)); // finally add all nodes to the model this->Update(); } this->endResetModel(); } } void QmitkDataStorageTreeModel::SetDataStorageDeleted() { this->SetDataStorage(nullptr); } void QmitkDataStorageTreeModel::AddNodeInternal(const mitk::DataNode *node) { - if (node == nullptr || m_DataStorage.IsExpired() || !m_DataStorage.Lock()->Exists(node) || m_Root->Find(node) != nullptr) + auto dataStorage = m_DataStorage.Lock(); + + if (node == nullptr || dataStorage.IsNull() || !dataStorage->Exists(node) || m_Root->Find(node) != nullptr) return; // find out if we have a root node TreeItem *parentTreeItem = m_Root; QModelIndex index; mitk::DataNode *parentDataNode = this->GetParentNode(node); if (parentDataNode) // no top level data node { parentTreeItem = m_Root->Find(parentDataNode); // find the corresponding tree item if (!parentTreeItem) { this->AddNode(parentDataNode); parentTreeItem = m_Root->Find(parentDataNode); if (!parentTreeItem) return; } // get the index of this parent with the help of the grand parent index = this->createIndex(parentTreeItem->GetIndex(), 0, parentTreeItem); } // add node if (m_PlaceNewNodesOnTop) { // emit beginInsertRows event beginInsertRows(index, 0, 0); parentTreeItem->InsertChild(new TreeItem(const_cast(node)), 0); } else { int firstRowWithASiblingBelow = 0; int nodeLayer = -1; node->GetIntProperty("layer", nodeLayer); for (TreeItem* siblingTreeItem: parentTreeItem->GetChildren()) { int siblingLayer = -1; if (mitk::DataNode* siblingNode = siblingTreeItem->GetDataNode()) { siblingNode->GetIntProperty("layer", siblingLayer); } if (nodeLayer > siblingLayer) { break; } ++firstRowWithASiblingBelow; } beginInsertRows(index, firstRowWithASiblingBelow, firstRowWithASiblingBelow); parentTreeItem->InsertChild(new TreeItem(const_cast(node)), firstRowWithASiblingBelow); } // emit endInsertRows event endInsertRows(); if(m_PlaceNewNodesOnTop) { this->AdjustLayerProperty(); } } void QmitkDataStorageTreeModel::AddNode(const mitk::DataNode *node) { - if (node == nullptr || m_BlockDataStorageEvents || m_DataStorage.IsExpired() || !m_DataStorage.Lock()->Exists(node) || + auto dataStorage = m_DataStorage.Lock(); + + if (node == nullptr || m_BlockDataStorageEvents || dataStorage.IsNull() || !dataStorage->Exists(node) || m_Root->Find(node) != nullptr) return; this->AddNodeInternal(node); } void QmitkDataStorageTreeModel::SetPlaceNewNodesOnTop(bool _PlaceNewNodesOnTop) { m_PlaceNewNodesOnTop = _PlaceNewNodesOnTop; } void QmitkDataStorageTreeModel::RemoveNodeInternal(const mitk::DataNode *node) { if (!m_Root) return; TreeItem *treeItem = m_Root->Find(node); if (!treeItem) return; // return because there is no treeitem containing this node TreeItem *parentTreeItem = treeItem->GetParent(); QModelIndex parentIndex = this->IndexFromTreeItem(parentTreeItem); // emit beginRemoveRows event (QModelIndex is empty because we dont have a tree model) this->beginRemoveRows(parentIndex, treeItem->GetIndex(), treeItem->GetIndex()); // remove node std::vector children = treeItem->GetChildren(); delete treeItem; // emit endRemoveRows event endRemoveRows(); // move all children of deleted node into its parent for (std::vector::iterator it = children.begin(); it != children.end(); it++) { // emit beginInsertRows event beginInsertRows(parentIndex, parentTreeItem->GetChildCount(), parentTreeItem->GetChildCount()); // add nodes again parentTreeItem->AddChild(*it); // emit endInsertRows event endInsertRows(); } this->AdjustLayerProperty(); } void QmitkDataStorageTreeModel::RemoveNode(const mitk::DataNode *node) { if (node == nullptr || m_BlockDataStorageEvents) return; this->RemoveNodeInternal(node); } void QmitkDataStorageTreeModel::SetNodeModified(const mitk::DataNode *node) { TreeItem *treeItem = m_Root->Find(node); if (treeItem) { TreeItem *parentTreeItem = treeItem->GetParent(); // as the root node should not be removed one should always have a parent item if (!parentTreeItem) return; QModelIndex index = this->createIndex(treeItem->GetIndex(), 0, treeItem); // now emit the dataChanged signal emit dataChanged(index, index); } } mitk::DataNode *QmitkDataStorageTreeModel::GetParentNode(const mitk::DataNode *node) const { mitk::DataNode *dataNode = nullptr; mitk::DataStorage::SetOfObjects::ConstPointer _Sources = m_DataStorage.Lock()->GetSources(node); if (_Sources->Size() > 0) dataNode = _Sources->front(); return dataNode; } bool QmitkDataStorageTreeModel::setData(const QModelIndex &index, const QVariant &value, int role) { mitk::DataNode *dataNode = this->TreeItemFromIndex(index)->GetDataNode(); if (!dataNode) return false; if (role == Qt::EditRole && !value.toString().isEmpty()) { dataNode->SetStringProperty("name", value.toString().toStdString().c_str()); mitk::PlanarFigure *planarFigure = dynamic_cast(dataNode->GetData()); if (planarFigure != nullptr) mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } else if (role == Qt::CheckStateRole) { // Please note: value.toInt() returns 2, independentely from the actual checkstate of the index element. // Therefore the checkstate is being estimated again here. QVariant qcheckstate = index.data(Qt::CheckStateRole); int checkstate = qcheckstate.toInt(); bool isVisible = bool(checkstate); dataNode->SetVisibility(!isVisible); emit nodeVisibilityChanged(); } // inform listeners about changes emit dataChanged(index, index); return true; } bool QmitkDataStorageTreeModel::setHeaderData(int /*section*/, Qt::Orientation /*orientation*/, const QVariant & /* value */, int /*role = Qt::EditRole*/) { return false; } void QmitkDataStorageTreeModel::AdjustLayerProperty() { /// transform the tree into an array and set the layer property descending std::vector vec; this->TreeToVector(m_Root, vec); int i = vec.size() - 1; for (std::vector::const_iterator it = vec.begin(); it != vec.end(); ++it) { mitk::DataNode::Pointer dataNode = (*it)->GetDataNode(); bool fixedLayer = false; if (!(dataNode->GetBoolProperty("fixedLayer", fixedLayer) && fixedLayer)) dataNode->SetIntProperty("layer", i); --i; } mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } void QmitkDataStorageTreeModel::TreeToVector(TreeItem *parent, std::vector &vec) const { TreeItem *current; for (int i = 0; i < parent->GetChildCount(); ++i) { current = parent->GetChild(i); this->TreeToVector(current, vec); vec.push_back(current); } } QModelIndex QmitkDataStorageTreeModel::IndexFromTreeItem(TreeItem *item) const { if (item == m_Root) return QModelIndex(); else return this->createIndex(item->GetIndex(), 0, item); } QList QmitkDataStorageTreeModel::GetNodeSet() const { QList res; if (m_Root) this->TreeToNodeSet(m_Root, res); return res; } void QmitkDataStorageTreeModel::TreeToNodeSet(TreeItem *parent, QList &vec) const { TreeItem *current; for (int i = 0; i < parent->GetChildCount(); ++i) { current = parent->GetChild(i); vec.push_back(current->GetDataNode()); this->TreeToNodeSet(current, vec); } } QModelIndex QmitkDataStorageTreeModel::GetIndex(const mitk::DataNode *node) const { if (m_Root) { TreeItem *item = m_Root->Find(node); if (item) return this->IndexFromTreeItem(item); } return QModelIndex(); } QList QmitkDataStorageTreeModel::ToTreeItemPtrList(const QMimeData *mimeData) { if (mimeData == nullptr || !mimeData->hasFormat(QmitkMimeTypes::DataStorageTreeItemPtrs)) { return QList(); } return ToTreeItemPtrList(mimeData->data(QmitkMimeTypes::DataStorageTreeItemPtrs)); } QList QmitkDataStorageTreeModel::ToTreeItemPtrList(const QByteArray &ba) { QList result; QDataStream ds(ba); while (!ds.atEnd()) { quintptr treeItemPtr; ds >> treeItemPtr; result.push_back(reinterpret_cast(treeItemPtr)); } return result; } void QmitkDataStorageTreeModel::Update() { auto datastorage = m_DataStorage.Lock(); if (datastorage.IsNotNull()) { mitk::DataStorage::SetOfObjects::ConstPointer _NodeSet = datastorage->GetAll(); /// Regardless the value of this preference, the new nodes must not be inserted /// at the top now, but at the position according to their layer. bool newNodesWereToBePlacedOnTop = m_PlaceNewNodesOnTop; m_PlaceNewNodesOnTop = false; for (const auto& node : *_NodeSet) { this->AddNodeInternal(node); } m_PlaceNewNodesOnTop = newNodesWereToBePlacedOnTop; /// Adjust the layers to ensure that derived nodes are above their sources. this->AdjustLayerProperty(); } } void QmitkDataStorageTreeModel::SetAllowHierarchyChange(bool allowHierarchyChange) { m_AllowHierarchyChange = allowHierarchyChange; } diff --git a/Modules/QtWidgets/src/QmitkPropertiesTableModel.cpp b/Modules/QtWidgets/src/QmitkPropertiesTableModel.cpp index a89b2af14c..5fd79e13bf 100644 --- a/Modules/QtWidgets/src/QmitkPropertiesTableModel.cpp +++ b/Modules/QtWidgets/src/QmitkPropertiesTableModel.cpp @@ -1,528 +1,530 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #include "QmitkPropertiesTableModel.h" //# Own includes #include "QmitkCustomVariants.h" #include "mitkColorProperty.h" #include "mitkEnumerationProperty.h" #include "mitkProperties.h" #include "mitkRenderingManager.h" #include "mitkStringProperty.h" //# Toolkit includes #include #include #include #include //# PUBLIC CTORS,DTOR QmitkPropertiesTableModel::QmitkPropertiesTableModel(QObject *parent, mitk::PropertyList::Pointer _PropertyList) : QAbstractTableModel(parent), m_PropertyList(nullptr), m_BlockEvents(false), m_SortDescending(false), m_FilterKeyWord("") { this->SetPropertyList(_PropertyList); } QmitkPropertiesTableModel::~QmitkPropertiesTableModel() { // remove all event listeners by setting the property list to 0 this->SetPropertyList(nullptr); } //# PUBLIC GETTER mitk::PropertyList::Pointer QmitkPropertiesTableModel::GetPropertyList() const { return m_PropertyList.Lock(); } Qt::ItemFlags QmitkPropertiesTableModel::flags(const QModelIndex &index) const { // no editing so far, return default (enabled, selectable) Qt::ItemFlags flags = QAbstractItemModel::flags(index); if (index.column() == PROPERTY_VALUE_COLUMN) { // there are also read only property items -> do not allow editing them if (index.data(Qt::EditRole).isValid()) flags |= Qt::ItemIsEditable; if (index.data(Qt::CheckStateRole).isValid()) flags |= Qt::ItemIsUserCheckable; } return flags; } QVariant QmitkPropertiesTableModel::headerData(int section, Qt::Orientation orientation, int role) const { if (role != Qt::DisplayRole) return QVariant(); if (orientation == Qt::Horizontal) { switch (section) { case PROPERTY_NAME_COLUMN: return tr("Name"); case PROPERTY_VALUE_COLUMN: return tr("Value"); default: return QVariant(); } } return QVariant(); } QVariant QmitkPropertiesTableModel::data(const QModelIndex &index, int role) const { // empty data by default QVariant data; if (!index.isValid() || m_SelectedProperties.empty() || index.row() > (int)(m_SelectedProperties.size() - 1)) return data; // the properties name if (index.column() == PROPERTY_NAME_COLUMN) { if (role == Qt::DisplayRole) data = QString::fromStdString(m_SelectedProperties[index.row()].first); } // the real properties value else if (index.column() == PROPERTY_VALUE_COLUMN) { mitk::BaseProperty *baseProp = m_SelectedProperties[index.row()].second; if (const mitk::ColorProperty *colorProp = dynamic_cast(baseProp)) { mitk::Color col = colorProp->GetColor(); QColor qcol((int)(col.GetRed() * 255), (int)(col.GetGreen() * 255), (int)(col.GetBlue() * 255)); if (role == Qt::DisplayRole) data.setValue(qcol); else if (role == Qt::EditRole) data.setValue(qcol); } else if (mitk::BoolProperty *boolProp = dynamic_cast(baseProp)) { if (role == Qt::CheckStateRole) data = boolProp->GetValue() ? Qt::Checked : Qt::Unchecked; } else if (mitk::StringProperty *stringProp = dynamic_cast(baseProp)) { if (role == Qt::DisplayRole) data.setValue(QString::fromStdString(stringProp->GetValue())); else if (role == Qt::EditRole) data.setValue(QString::fromStdString(stringProp->GetValue())); } else if (mitk::IntProperty *intProp = dynamic_cast(baseProp)) { if (role == Qt::DisplayRole) data.setValue(intProp->GetValue()); else if (role == Qt::EditRole) data.setValue(intProp->GetValue()); } else if (mitk::FloatProperty *floatProp = dynamic_cast(baseProp)) { if (role == Qt::DisplayRole) data.setValue(floatProp->GetValue()); else if (role == Qt::EditRole) data.setValue(floatProp->GetValue()); } else if (mitk::EnumerationProperty *enumerationProp = dynamic_cast(baseProp)) { if (role == Qt::DisplayRole) data.setValue(QString::fromStdString(baseProp->GetValueAsString())); else if (role == Qt::EditRole) { QStringList values; for (auto it = enumerationProp->Begin(); it != enumerationProp->End(); it++) { values << QString::fromStdString(it->second); } data.setValue(values); } } else { if (role == Qt::DisplayRole) data.setValue(QString::fromStdString(m_SelectedProperties[index.row()].second->GetValueAsString())); } } return data; } int QmitkPropertiesTableModel::rowCount(const QModelIndex & /*parent*/) const { // return the number of properties in the properties list. return m_SelectedProperties.size(); } int QmitkPropertiesTableModel::columnCount(const QModelIndex & /*parent*/) const { return 2; } //# PUBLIC SETTER void QmitkPropertiesTableModel::SetPropertyList(mitk::PropertyList *_PropertyList) { // if propertylist really changed if (m_PropertyList != _PropertyList) { + auto propertyList = m_PropertyList.Lock(); // Remove delete listener if there was a propertylist before - if (!m_PropertyList.IsExpired()) - m_PropertyList.Lock()->RemoveObserver(m_PropertyListDeleteObserverTag); + if (propertyList.IsNotNull()) + propertyList->RemoveObserver(m_PropertyListDeleteObserverTag); // set new list m_PropertyList = _PropertyList; + propertyList = m_PropertyList.Lock(); - if (!m_PropertyList.IsExpired()) + if (propertyList.IsNotNull()) { auto command = itk::SimpleMemberCommand::New(); command->SetCallbackFunction(this, &QmitkPropertiesTableModel::PropertyListDelete); - m_PropertyListDeleteObserverTag = m_PropertyList.Lock()->AddObserver(itk::DeleteEvent(), command); + m_PropertyListDeleteObserverTag = propertyList->AddObserver(itk::DeleteEvent(), command); } this->Reset(); } } void QmitkPropertiesTableModel::PropertyListDelete() { if (!m_BlockEvents) { m_BlockEvents = true; this->Reset(); m_BlockEvents = false; } } void QmitkPropertiesTableModel::PropertyModified(const itk::Object *caller, const itk::EventObject & /*event*/) { if (!m_BlockEvents) { m_BlockEvents = true; int row = this->FindProperty(dynamic_cast(caller)); QModelIndex indexOfChangedProperty = index(row, 1); emit dataChanged(indexOfChangedProperty, indexOfChangedProperty); m_BlockEvents = false; } } void QmitkPropertiesTableModel::PropertyDelete(const itk::Object *caller, const itk::EventObject & /*event*/) { if (!m_BlockEvents) { m_BlockEvents = true; int row = this->FindProperty(dynamic_cast(caller)); if (row >= 0) this->Reset(); m_BlockEvents = false; } } bool QmitkPropertiesTableModel::setData(const QModelIndex &index, const QVariant &value, int role) { // TODO: check 'role' condition if (index.isValid() && !m_SelectedProperties.empty() && index.row() < (int)(m_SelectedProperties.size()) && (role == Qt::EditRole || role == Qt::CheckStateRole)) { // block all events now! m_BlockEvents = true; auto propertyList = m_PropertyList.Lock(); // the properties name if (index.column() == PROPERTY_VALUE_COLUMN) { mitk::BaseProperty *baseProp = m_SelectedProperties[index.row()].second; if (mitk::ColorProperty *colorProp = dynamic_cast(baseProp)) { QColor qcolor = value.value(); if (!qcolor.isValid()) return false; mitk::Color col = colorProp->GetColor(); col.SetRed(qcolor.red() / 255.0); col.SetGreen(qcolor.green() / 255.0); col.SetBlue(qcolor.blue() / 255.0); colorProp->SetColor(col); propertyList->InvokeEvent(itk::ModifiedEvent()); propertyList->Modified(); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } else if (mitk::BoolProperty *boolProp = dynamic_cast(baseProp)) { boolProp->SetValue(value.toInt() == Qt::Checked ? true : false); propertyList->InvokeEvent(itk::ModifiedEvent()); propertyList->Modified(); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } else if (mitk::StringProperty *stringProp = dynamic_cast(baseProp)) { stringProp->SetValue((value.value()).toStdString()); propertyList->InvokeEvent(itk::ModifiedEvent()); propertyList->Modified(); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } else if (mitk::IntProperty *intProp = dynamic_cast(baseProp)) { int intValue = value.value(); if (intValue != intProp->GetValue()) { intProp->SetValue(intValue); propertyList->InvokeEvent(itk::ModifiedEvent()); propertyList->Modified(); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } } else if (mitk::FloatProperty *floatProp = dynamic_cast(baseProp)) { float floatValue = value.value(); if (floatValue != floatProp->GetValue()) { floatProp->SetValue(floatValue); propertyList->InvokeEvent(itk::ModifiedEvent()); propertyList->Modified(); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } } else if (mitk::EnumerationProperty *enumerationProp = dynamic_cast(baseProp)) { std::string activatedItem = value.value().toStdString(); if (activatedItem != enumerationProp->GetValueAsString()) { if (enumerationProp->IsValidEnumerationValue(activatedItem)) { enumerationProp->SetValue(activatedItem); propertyList->InvokeEvent(itk::ModifiedEvent()); propertyList->Modified(); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } } } } // property was changed by us, now we can accept property changes triggered by someone else m_BlockEvents = false; emit dataChanged(index, index); return true; } return false; } void QmitkPropertiesTableModel::sort(int column, Qt::SortOrder order /*= Qt::AscendingOrder */) { bool sortDescending = (order == Qt::DescendingOrder) ? true : false; // do not sort twice !!! (dont know why, but qt calls this func twice. STUPID!) if (sortDescending != m_SortDescending) { m_SortDescending = sortDescending; PropertyDataSetCompareFunction::CompareCriteria _CompareCriteria = PropertyDataSetCompareFunction::CompareByName; PropertyDataSetCompareFunction::CompareOperator _CompareOperator = m_SortDescending ? PropertyDataSetCompareFunction::Greater : PropertyDataSetCompareFunction::Less; if (column == PROPERTY_VALUE_COLUMN) _CompareCriteria = PropertyDataSetCompareFunction::CompareByValue; PropertyDataSetCompareFunction compareFunc(_CompareCriteria, _CompareOperator); std::sort(m_SelectedProperties.begin(), m_SelectedProperties.end(), compareFunc); QAbstractTableModel::beginResetModel(); QAbstractTableModel::endResetModel(); } } //# PROTECTED GETTER int QmitkPropertiesTableModel::FindProperty(const mitk::BaseProperty *_Property) const { int row = -1; if (_Property) { // search for property that changed and emit datachanged on the corresponding ModelIndex std::vector::const_iterator propertyIterator; for (propertyIterator = m_SelectedProperties.begin(); propertyIterator != m_SelectedProperties.end(); propertyIterator++) { if (propertyIterator->second == _Property) break; } if (propertyIterator != m_SelectedProperties.end()) row = std::distance(m_SelectedProperties.begin(), propertyIterator); } return row; } //# PROTECTED SETTER void QmitkPropertiesTableModel::AddSelectedProperty(PropertyDataSet &_PropertyDataSet) { // subscribe for modified event itk::MemberCommand::Pointer _PropertyDataSetModifiedCommand = itk::MemberCommand::New(); _PropertyDataSetModifiedCommand->SetCallbackFunction(this, &QmitkPropertiesTableModel::PropertyModified); m_PropertyModifiedObserverTags.push_back( _PropertyDataSet.second->AddObserver(itk::ModifiedEvent(), _PropertyDataSetModifiedCommand)); // subscribe for delete event itk::MemberCommand::Pointer _PropertyDataSetDeleteCommand = itk::MemberCommand::New(); _PropertyDataSetDeleteCommand->SetCallbackFunction(this, &QmitkPropertiesTableModel::PropertyDelete); m_PropertyDeleteObserverTags.push_back( _PropertyDataSet.second->AddObserver(itk::DeleteEvent(), _PropertyDataSetDeleteCommand)); // add to the selection m_SelectedProperties.push_back(_PropertyDataSet); } void QmitkPropertiesTableModel::RemoveSelectedProperty(unsigned int _Index) { PropertyDataSet &_PropertyDataSet = m_SelectedProperties.at(_Index); // remove modified event listener _PropertyDataSet.second->RemoveObserver(m_PropertyModifiedObserverTags[_Index]); m_PropertyModifiedObserverTags.erase(m_PropertyModifiedObserverTags.begin() + _Index); // remove delete event listener _PropertyDataSet.second->RemoveObserver(m_PropertyDeleteObserverTags[_Index]); m_PropertyDeleteObserverTags.erase(m_PropertyDeleteObserverTags.begin() + _Index); // remove from selection m_SelectedProperties.erase(m_SelectedProperties.begin() + _Index); } void QmitkPropertiesTableModel::Reset() { // remove all selected properties while (!m_SelectedProperties.empty()) { this->RemoveSelectedProperty(m_SelectedProperties.size() - 1); } std::vector allPredicates; - if (!m_PropertyList.IsExpired()) - { - auto propertyList = m_PropertyList.Lock(); + auto propertyList = m_PropertyList.Lock(); + if (propertyList.IsNotNull()) + { // first of all: collect all properties from the list for (auto it = propertyList->GetMap()->begin(); it != propertyList->GetMap()->end(); it++) { allPredicates.push_back(*it); //% TODO } } // make a subselection if a keyword is specified if (!m_FilterKeyWord.empty()) { std::vector subSelection; for (auto it = allPredicates.begin(); it != allPredicates.end(); it++) { // add this to the selection if it is matched by the keyword if ((*it).first.find(m_FilterKeyWord) != std::string::npos) subSelection.push_back((*it)); } allPredicates.clear(); allPredicates = subSelection; } PropertyDataSet tmpPropertyDataSet; // add all selected now to the Model for (auto it = allPredicates.begin(); it != allPredicates.end(); it++) { tmpPropertyDataSet = *it; this->AddSelectedProperty(tmpPropertyDataSet); } // sort the list as indicated by m_SortDescending this->sort(m_SortDescending); // model was resetted QAbstractTableModel::beginResetModel(); QAbstractTableModel::endResetModel(); } void QmitkPropertiesTableModel::SetFilterPropertiesKeyWord(std::string _FilterKeyWord) { m_FilterKeyWord = _FilterKeyWord; this->Reset(); } QmitkPropertiesTableModel::PropertyDataSetCompareFunction::PropertyDataSetCompareFunction( CompareCriteria _CompareCriteria, CompareOperator _CompareOperator) : m_CompareCriteria(_CompareCriteria), m_CompareOperator(_CompareOperator) { } bool QmitkPropertiesTableModel::PropertyDataSetCompareFunction::operator()(const PropertyDataSet &_Left, const PropertyDataSet &_Right) const { switch (m_CompareCriteria) { case CompareByValue: if (m_CompareOperator == Less) return (_Left.second->GetValueAsString() < _Right.second->GetValueAsString()); else return (_Left.second->GetValueAsString() > _Right.second->GetValueAsString()); break; // CompareByName: default: if (m_CompareOperator == Less) return (_Left.first < _Right.first); else return (_Left.first > _Right.first); break; } } QmitkPropertiesTableModel::PropertyListElementFilterFunction::PropertyListElementFilterFunction( const std::string &_FilterKeyWord) : m_FilterKeyWord(_FilterKeyWord) { } bool QmitkPropertiesTableModel::PropertyListElementFilterFunction::operator()(const PropertyDataSet &_Elem) const { if (m_FilterKeyWord.empty()) return true; return (_Elem.first.find(m_FilterKeyWord) == 0); } diff --git a/Modules/QtWidgets/src/QmitkPropertyItemDelegate.cpp b/Modules/QtWidgets/src/QmitkPropertyItemDelegate.cpp index ed90dc0aa3..9eae1448e9 100644 --- a/Modules/QtWidgets/src/QmitkPropertyItemDelegate.cpp +++ b/Modules/QtWidgets/src/QmitkPropertyItemDelegate.cpp @@ -1,380 +1,382 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #include "QmitkPropertyItemDelegate.h" #include "QmitkPropertyItemModel.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include QmitkColorWidget::QmitkColorWidget(QWidget *parent) : QWidget(parent), m_LineEdit(new QLineEdit), m_Button(new QToolButton) { m_LineEdit->setText(m_Color.name()); m_Button->setText("..."); QHBoxLayout *layout = new QHBoxLayout; layout->setContentsMargins(0, 0, 0, 0); layout->setSpacing(0); layout->addWidget(m_LineEdit); layout->addWidget(m_Button); this->setFocusProxy(m_LineEdit); this->setLayout(layout); connect(m_LineEdit, SIGNAL(editingFinished()), this, SLOT(OnLineEditEditingFinished())); connect(m_Button, SIGNAL(clicked()), this, SLOT(OnButtonClicked())); } QmitkColorWidget::~QmitkColorWidget() { } QColor QmitkColorWidget::GetColor() const { return m_Color; } void QmitkColorWidget::SetColor(QColor color) { m_Color = color; m_LineEdit->setText(color.name()); } void QmitkColorWidget::OnLineEditEditingFinished() { if (!QColor::isValidColor(m_LineEdit->text())) m_LineEdit->setText("#000000"); m_Color.setNamedColor(m_LineEdit->text()); } void QmitkColorWidget::OnButtonClicked() { QColor color = QColorDialog::getColor(m_Color, QApplication::activeWindow()); if (color.isValid()) { this->SetColor(color); emit ColorPicked(); } } QmitkComboBoxListView::QmitkComboBoxListView(QComboBox *comboBox) : m_ComboBox(comboBox) { } QmitkComboBoxListView::~QmitkComboBoxListView() { } void QmitkComboBoxListView::paintEvent(QPaintEvent *event) { if (m_ComboBox != nullptr) { QStyleOptionComboBox option; option.initFrom(m_ComboBox); option.editable = m_ComboBox->isEditable(); if (m_ComboBox->style()->styleHint(QStyle::SH_ComboBox_Popup, &option, m_ComboBox)) { QStyleOptionMenuItem menuOption; menuOption.initFrom(this); menuOption.palette = this->palette(); menuOption.state = QStyle::State_None; menuOption.checkType = QStyleOptionMenuItem::NotCheckable; menuOption.menuRect = event->rect(); menuOption.maxIconWidth = 0; menuOption.tabWidth = 0; QPainter painter(this->viewport()); m_ComboBox->style()->drawControl(QStyle::CE_MenuEmptyArea, &menuOption, &painter, this); } } QListView::paintEvent(event); } void QmitkComboBoxListView::resizeEvent(QResizeEvent *event) { int width = this->viewport()->width(); int height = this->contentsSize().height(); this->resizeContents(width, height); QListView::resizeEvent(event); } QStyleOptionViewItem QmitkComboBoxListView::viewOptions() const { QStyleOptionViewItem option = QListView::viewOptions(); option.showDecorationSelected = true; if (m_ComboBox != nullptr) option.font = m_ComboBox->font(); return option; } class PropertyEqualTo { public: PropertyEqualTo(const mitk::BaseProperty *property) : m_Property(property) {} bool operator()(const mitk::PropertyList::PropertyMapElementType &pair) const { return pair.second.GetPointer() == m_Property; } private: const mitk::BaseProperty *m_Property; }; QmitkPropertyItemDelegate::QmitkPropertyItemDelegate(QObject *parent) : QStyledItemDelegate(parent) { } QmitkPropertyItemDelegate::~QmitkPropertyItemDelegate() { } QWidget *QmitkPropertyItemDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const { QVariant data = index.data(Qt::EditRole); if (data.isValid()) { if (data.type() == QVariant::Int) { QSpinBox *spinBox = new QSpinBox(parent); mitk::CoreServicePointer extensions(mitk::CoreServices::GetPropertyExtensions()); std::string name = this->GetPropertyName(index); if (!name.empty() && extensions->HasExtension(name)) { mitk::IntPropertyExtension::Pointer extension = dynamic_cast(extensions->GetExtension(name).GetPointer()); if (extension.IsNotNull()) { spinBox->setMinimum(extension->GetMinimum()); spinBox->setMaximum(extension->GetMaximum()); spinBox->setSingleStep(extension->GetSingleStep()); } } connect(spinBox, SIGNAL(editingFinished()), this, SLOT(OnSpinBoxEditingFinished())); return spinBox; } if (data.type() == QVariant::Double || static_cast(data.type()) == QMetaType::Float) { QDoubleSpinBox *spinBox = new QDoubleSpinBox(parent); mitk::CoreServicePointer extensions(mitk::CoreServices::GetPropertyExtensions()); std::string name = this->GetPropertyName(index); if (!name.empty() && extensions->HasExtension(name)) { mitk::FloatPropertyExtension::Pointer extension = dynamic_cast(extensions->GetExtension(name).GetPointer()); if (extension.IsNotNull()) { spinBox->setMinimum(extension->GetMinimum()); spinBox->setMaximum(extension->GetMaximum()); spinBox->setSingleStep(extension->GetSingleStep()); spinBox->setDecimals(extension->GetDecimals()); } } else { spinBox->setSingleStep(0.1); spinBox->setDecimals(4); } if (name == "opacity") // TODO { spinBox->setMinimum(0.0); spinBox->setMaximum(1.0); } connect(spinBox, SIGNAL(editingFinished()), this, SLOT(OnSpinBoxEditingFinished())); return spinBox; } if (data.type() == QVariant::StringList) { QComboBox *comboBox = new QComboBox(parent); comboBox->setView(new QmitkComboBoxListView(comboBox)); comboBox->addItems(data.toStringList()); connect(comboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(OnComboBoxCurrentIndexChanged(int))); return comboBox; } if (data.type() == QVariant::Color) { QmitkColorWidget *colorWidget = new QmitkColorWidget(parent); connect(colorWidget, SIGNAL(ColorPicked()), this, SLOT(OnColorPicked())); return colorWidget; } } return QStyledItemDelegate::createEditor(parent, option, index); } std::string QmitkPropertyItemDelegate::GetPropertyName(const QModelIndex &index) const { - if (!m_PropertyList.IsExpired()) + auto propertyList = m_PropertyList.Lock(); + + if (propertyList.IsNotNull()) { mitk::BaseProperty *property = reinterpret_cast(index.data(mitk::PropertyRole).value()); - const mitk::PropertyList::PropertyMap *propertyMap = m_PropertyList.Lock()->GetMap(); + const mitk::PropertyList::PropertyMap *propertyMap = propertyList->GetMap(); mitk::PropertyList::PropertyMap::const_iterator it = std::find_if(propertyMap->begin(), propertyMap->end(), PropertyEqualTo(property)); if (it != propertyMap->end()) return it->first; } return ""; } void QmitkPropertyItemDelegate::OnComboBoxCurrentIndexChanged(int) { QComboBox *comboBox = qobject_cast(sender()); emit commitData(comboBox); emit closeEditor(comboBox); } void QmitkPropertyItemDelegate::OnSpinBoxEditingFinished() { QAbstractSpinBox *spinBox = qobject_cast(sender()); emit commitData(spinBox); emit closeEditor(spinBox); } void QmitkPropertyItemDelegate::OnColorPicked() { QmitkColorWidget *colorWidget = qobject_cast(sender()); emit commitData(colorWidget); emit closeEditor(colorWidget); } void QmitkPropertyItemDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const { QVariant data = index.data(); if (index.column() == 1 && data.type() == QVariant::Color) { painter->fillRect(option.rect, data.value()); return; } QStyledItemDelegate::paint(painter, option, index); } void QmitkPropertyItemDelegate::setEditorData(QWidget *editor, const QModelIndex &index) const { QVariant data = index.data(Qt::EditRole); if (!data.isValid()) return; if (data.type() == QVariant::StringList) { QComboBox *comboBox = qobject_cast(editor); comboBox->setCurrentIndex(comboBox->findText(index.data().toString())); } if (data.type() == QVariant::Color) { QmitkColorWidget *colorWidget = qobject_cast(editor); colorWidget->SetColor(data.value()); } else { QStyledItemDelegate::setEditorData(editor, index); } } void QmitkPropertyItemDelegate::setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const { QVariant data = index.data(Qt::EditRole); if (!data.isValid()) return; if (data.type() == QVariant::Int) { QSpinBox *spinBox = qobject_cast(editor); model->setData(index, spinBox->value()); } else if (data.type() == QVariant::Double) { QDoubleSpinBox *spinBox = qobject_cast(editor); model->setData(index, spinBox->value()); } else if (static_cast(data.type()) == QMetaType::Float) { QDoubleSpinBox *spinBox = qobject_cast(editor); model->setData(index, static_cast(spinBox->value())); } else if (data.type() == QVariant::StringList) { QComboBox *comboBox = qobject_cast(editor); model->setData(index, comboBox->currentText()); } else if (data.type() == QVariant::Color) { QmitkColorWidget *colorWidget = qobject_cast(editor); model->setData(index, colorWidget->GetColor()); } else { QStyledItemDelegate::setModelData(editor, model, index); } } void QmitkPropertyItemDelegate::SetPropertyList(mitk::PropertyList *propertyList) { if (m_PropertyList != propertyList) m_PropertyList = propertyList; } diff --git a/Modules/QtWidgets/src/QmitkPropertyItemModel.cpp b/Modules/QtWidgets/src/QmitkPropertyItemModel.cpp index 66b89810ff..10514fbe2a 100644 --- a/Modules/QtWidgets/src/QmitkPropertyItemModel.cpp +++ b/Modules/QtWidgets/src/QmitkPropertyItemModel.cpp @@ -1,474 +1,478 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #include "QmitkPropertyItemModel.h" #include "QmitkPropertyItem.h" #include #include #include #include #include #include #include #include namespace { QColor MitkToQt(const mitk::Color& color) { return QColor(color.GetRed() * 255, color.GetGreen() * 255, color.GetBlue() * 255); } mitk::BaseProperty* GetBaseProperty(const QVariant& data) { return data.isValid() ? reinterpret_cast(data.value()) : nullptr; } mitk::Color QtToMitk(const QColor& color) { mitk::Color mitkColor; mitkColor.SetRed(color.red() / 255.0f); mitkColor.SetGreen(color.green() / 255.0f); mitkColor.SetBlue(color.blue() / 255.0f); return mitkColor; } } class PropertyEqualTo { public: PropertyEqualTo(const mitk::BaseProperty *property) : m_Property(property) {} bool operator()(const mitk::PropertyList::PropertyMapElementType &pair) const { return pair.second.GetPointer() == m_Property; } private: const mitk::BaseProperty *m_Property; }; QmitkPropertyItemModel::QmitkPropertyItemModel(QObject *parent) : QAbstractItemModel(parent), m_PropertyAliases(mitk::CoreServices::GetPropertyAliases()), m_PropertyFilters(mitk::CoreServices::GetPropertyFilters()) { this->CreateRootItem(); } QmitkPropertyItemModel::~QmitkPropertyItemModel() { this->SetNewPropertyList(nullptr); } int QmitkPropertyItemModel::columnCount(const QModelIndex &parent) const { if (parent.isValid()) return static_cast(parent.internalPointer())->GetColumnCount(); else return m_RootItem->GetColumnCount(); } void QmitkPropertyItemModel::CreateRootItem() { QList rootData; rootData << "Property" << "Value"; m_RootItem.reset(new QmitkPropertyItem(rootData)); this->beginResetModel(); this->endResetModel(); } QVariant QmitkPropertyItemModel::data(const QModelIndex &index, int role) const { if (!index.isValid()) return QVariant(); mitk::BaseProperty *property = index.column() == 1 ? GetBaseProperty(static_cast(index.internalPointer())->GetData(1)) : nullptr; if (role == Qt::DisplayRole) { if (index.column() == 0) { return static_cast(index.internalPointer())->GetData(0); } else if (index.column() == 1 && property != nullptr) { if (auto colorProperty = dynamic_cast(property)) return MitkToQt(colorProperty->GetValue()); else if (dynamic_cast(property) == nullptr) return QString::fromStdString(property->GetValueAsString()); } } else if (index.column() == 1 && property != nullptr) { if (role == Qt::CheckStateRole) { if (auto boolProperty = dynamic_cast(property)) return boolProperty->GetValue() ? Qt::Checked : Qt::Unchecked; } else if (role == Qt::EditRole) { if (dynamic_cast(property) != nullptr) { return QString::fromStdString(property->GetValueAsString()); } else if (auto intProperty = dynamic_cast(property)) { return intProperty->GetValue(); } else if (auto floatProperty = dynamic_cast(property)) { return floatProperty->GetValue(); } else if (auto doubleProperty = dynamic_cast(property)) { return doubleProperty->GetValue(); } else if (auto enumProperty = dynamic_cast(property)) { QStringList values; for (mitk::EnumerationProperty::EnumConstIterator it = enumProperty->Begin(); it != enumProperty->End(); it++) values << QString::fromStdString(it->second); return values; } else if (auto colorProperty = dynamic_cast(property)) { return MitkToQt(colorProperty->GetValue()); } } else if (role == mitk::PropertyRole) { return QVariant::fromValue(property); } } return QVariant(); } QModelIndex QmitkPropertyItemModel::FindProperty(const mitk::BaseProperty *property) { if (property == nullptr) return QModelIndex(); - if (m_PropertyList.IsExpired()) + auto propertyList = m_PropertyList.Lock(); + + if (propertyList.IsNull()) return QModelIndex(); - auto propertyMap = m_PropertyList.Lock()->GetMap(); + auto propertyMap = propertyList->GetMap(); auto it = std::find_if(propertyMap->begin(), propertyMap->end(), PropertyEqualTo(property)); if (it == propertyMap->end()) return QModelIndex(); QString name = QString::fromStdString(it->first); if (!name.contains('.')) { QModelIndexList item = this->match(index(0, 0), Qt::DisplayRole, name, 1, Qt::MatchExactly); if (!item.empty()) return item[0]; } else { QStringList names = name.split('.'); QModelIndexList items = this->match(index(0, 0), Qt::DisplayRole, names.last(), -1, Qt::MatchRecursive | Qt::MatchExactly); for (auto item : qAsConst(items)) { QModelIndex candidate = item; for (int i = names.length() - 1; i != 0; --i) { QModelIndex parent = item.parent(); if (parent.parent() == QModelIndex()) { if (parent.data() != names.first()) break; return candidate; } if (parent.data() != names[i - 1]) break; item = parent; } } } return QModelIndex(); } Qt::ItemFlags QmitkPropertyItemModel::flags(const QModelIndex &index) const { Qt::ItemFlags flags = QAbstractItemModel::flags(index); if (index.column() == 1) { if (index.data(Qt::EditRole).isValid()) flags |= Qt::ItemIsEditable; if (index.data(Qt::CheckStateRole).isValid()) flags |= Qt::ItemIsUserCheckable; } return flags; } mitk::PropertyList *QmitkPropertyItemModel::GetPropertyList() const { return m_PropertyList.Lock().GetPointer(); } QVariant QmitkPropertyItemModel::headerData(int section, Qt::Orientation orientation, int role) const { if (orientation == Qt::Horizontal && role == Qt::DisplayRole) return m_RootItem->GetData(section); return QVariant(); } QModelIndex QmitkPropertyItemModel::index(int row, int column, const QModelIndex &parent) const { if (!this->hasIndex(row, column, parent)) return QModelIndex(); QmitkPropertyItem *parentItem = parent.isValid() ? static_cast(parent.internalPointer()) : m_RootItem.get(); QmitkPropertyItem *childItem = parentItem->GetChild(row); return childItem != nullptr ? this->createIndex(row, column, childItem) : QModelIndex(); } void QmitkPropertyItemModel::OnPropertyListModified() { this->SetNewPropertyList(m_PropertyList.Lock()); } void QmitkPropertyItemModel::OnPropertyListDeleted() { this->CreateRootItem(); } void QmitkPropertyItemModel::OnPropertyModified(const itk::Object *property, const itk::EventObject &) { QModelIndex index = this->FindProperty(static_cast(property)); if (index != QModelIndex()) emit dataChanged(index, index); } QModelIndex QmitkPropertyItemModel::parent(const QModelIndex &child) const { if (!child.isValid()) return QModelIndex(); QmitkPropertyItem *parentItem = static_cast(child.internalPointer())->GetParent(); if (parentItem == m_RootItem.get()) return QModelIndex(); return this->createIndex(parentItem->GetRow(), 0, parentItem); } int QmitkPropertyItemModel::rowCount(const QModelIndex &parent) const { if (parent.column() > 0) return 0; QmitkPropertyItem *parentItem = parent.isValid() ? static_cast(parent.internalPointer()) : m_RootItem.get(); return parentItem->GetChildCount(); } bool QmitkPropertyItemModel::setData(const QModelIndex &index, const QVariant &value, int role) { if (!index.isValid() || index.column() != 1 || (role != Qt::EditRole && role != Qt::CheckStateRole)) return false; mitk::BaseProperty *property = GetBaseProperty(static_cast(index.internalPointer())->GetData(1)); if (property == nullptr) return false; if (mitk::BoolProperty *boolProperty = dynamic_cast(property)) { boolProperty->SetValue(value.toInt() == Qt::Checked ? true : false); } else if (mitk::StringProperty *stringProperty = dynamic_cast(property)) { stringProperty->SetValue(value.toString().toStdString()); } else if (mitk::IntProperty *intProperty = dynamic_cast(property)) { intProperty->SetValue(value.toInt()); } else if (mitk::FloatProperty *floatProperty = dynamic_cast(property)) { floatProperty->SetValue(value.toFloat()); } else if (mitk::DoubleProperty *doubleProperty = dynamic_cast(property)) { doubleProperty->SetValue(value.toDouble()); } else if (mitk::EnumerationProperty *enumProperty = dynamic_cast(property)) { std::string selection = value.toString().toStdString(); if (selection != enumProperty->GetValueAsString() && enumProperty->IsValidEnumerationValue(selection)) enumProperty->SetValue(selection); } else if (mitk::ColorProperty *colorProperty = dynamic_cast(property)) { colorProperty->SetValue(QtToMitk(value.value())); } auto propertyList = m_PropertyList.Lock(); propertyList->Modified(); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); return true; } void QmitkPropertyItemModel::SetNewPropertyList(mitk::PropertyList *newPropertyList) { typedef mitk::PropertyList::PropertyMap PropertyMap; this->beginResetModel(); - if (!m_PropertyList.IsExpired()) - { - auto propertyList = m_PropertyList.Lock(); + auto propertyList = m_PropertyList.Lock(); + if (propertyList.IsNotNull()) + { propertyList->RemoveObserver(m_PropertyListDeletedTag); propertyList->RemoveObserver(m_PropertyListModifiedTag); - const PropertyMap *propertyMap = m_PropertyList.Lock()->GetMap(); + const PropertyMap *propertyMap = propertyList->GetMap(); for (PropertyMap::const_iterator propertyIt = propertyMap->begin(); propertyIt != propertyMap->end(); ++propertyIt) { std::map::const_iterator tagIt = m_PropertyModifiedTags.find(propertyIt->first); if (tagIt != m_PropertyModifiedTags.end()) propertyIt->second->RemoveObserver(tagIt->second); tagIt = m_PropertyDeletedTags.find(propertyIt->first); if (tagIt != m_PropertyDeletedTags.end()) propertyIt->second->RemoveObserver(tagIt->second); } m_PropertyModifiedTags.clear(); m_PropertyDeletedTags.clear(); } m_PropertyList = newPropertyList; + propertyList = m_PropertyList.Lock(); - if (!m_PropertyList.IsExpired()) + if (propertyList.IsNotNull()) { auto onPropertyListModified = itk::SimpleMemberCommand::New(); onPropertyListModified->SetCallbackFunction(this, &QmitkPropertyItemModel::OnPropertyListModified); - m_PropertyListModifiedTag = m_PropertyList.Lock()->AddObserver(itk::ModifiedEvent(), onPropertyListModified); + m_PropertyListModifiedTag = propertyList->AddObserver(itk::ModifiedEvent(), onPropertyListModified); auto onPropertyListDeleted = itk::SimpleMemberCommand::New(); onPropertyListDeleted->SetCallbackFunction(this, &QmitkPropertyItemModel::OnPropertyListDeleted); - m_PropertyListDeletedTag = m_PropertyList.Lock()->AddObserver(itk::DeleteEvent(), onPropertyListDeleted); + m_PropertyListDeletedTag = propertyList->AddObserver(itk::DeleteEvent(), onPropertyListDeleted); auto modifiedCommand = itk::MemberCommand::New(); modifiedCommand->SetCallbackFunction(this, &QmitkPropertyItemModel::OnPropertyModified); const PropertyMap *propertyMap = m_PropertyList.Lock()->GetMap(); for (PropertyMap::const_iterator it = propertyMap->begin(); it != propertyMap->end(); ++it) m_PropertyModifiedTags.insert( std::make_pair(it->first, it->second->AddObserver(itk::ModifiedEvent(), modifiedCommand))); } this->CreateRootItem(); + propertyList = m_PropertyList.Lock(); - if (m_PropertyList != nullptr && !m_PropertyList.Lock()->IsEmpty()) + if (propertyList.IsNotNull() && !propertyList->IsEmpty()) { mitk::PropertyList::PropertyMap filteredProperties; bool filterProperties = false; if (m_PropertyFilters->HasFilter() || m_PropertyFilters->HasFilter(m_ClassName.toStdString())) { - filteredProperties = m_PropertyFilters->ApplyFilter(*m_PropertyList.Lock()->GetMap(), m_ClassName.toStdString()); + filteredProperties = m_PropertyFilters->ApplyFilter(*propertyList->GetMap(), m_ClassName.toStdString()); filterProperties = true; } const mitk::PropertyList::PropertyMap *propertyMap = - !filterProperties ? m_PropertyList.Lock()->GetMap() : &filteredProperties; + !filterProperties ? propertyList->GetMap() : &filteredProperties; mitk::PropertyList::PropertyMap::const_iterator end = propertyMap->end(); for (mitk::PropertyList::PropertyMap::const_iterator iter = propertyMap->begin(); iter != end; ++iter) { std::vector aliases; aliases = m_PropertyAliases->GetAliases(iter->first, m_ClassName.toStdString()); if (aliases.empty() && !m_ClassName.isEmpty()) aliases = m_PropertyAliases->GetAliases(iter->first); if (aliases.empty()) { QList data; data << QString::fromStdString(iter->first) << QVariant::fromValue((reinterpret_cast(iter->second.GetPointer()))); m_RootItem->AppendChild(new QmitkPropertyItem(data)); } else { std::vector::const_iterator end = aliases.end(); for (std::vector::const_iterator aliasIter = aliases.begin(); aliasIter != end; ++aliasIter) { QList data; data << QString::fromStdString(*aliasIter) << QVariant::fromValue((reinterpret_cast(iter->second.GetPointer()))); m_RootItem->AppendChild(new QmitkPropertyItem(data)); } } } } this->endResetModel(); } void QmitkPropertyItemModel::SetPropertyList(mitk::PropertyList *propertyList, const QString &className) { if (m_PropertyList != propertyList) { m_ClassName = className; this->SetNewPropertyList(propertyList); } } void QmitkPropertyItemModel::Update() { this->SetNewPropertyList(m_PropertyList.Lock()); } diff --git a/Modules/QtWidgetsExt/src/QmitkPointListViewWidget.cpp b/Modules/QtWidgetsExt/src/QmitkPointListViewWidget.cpp index 347368f308..13a7321abe 100644 --- a/Modules/QtWidgetsExt/src/QmitkPointListViewWidget.cpp +++ b/Modules/QtWidgetsExt/src/QmitkPointListViewWidget.cpp @@ -1,256 +1,255 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #include "QmitkPointListViewWidget.h" #include "QmitkEditPointDialog.h" #include "QmitkPointListModel.h" #include "QmitkStdMultiWidget.h" #include "mitkInteractionConst.h" #include "mitkPointOperation.h" #include "mitkRenderingManager.h" #include QmitkPointListViewWidget::QmitkPointListViewWidget(QWidget *parent) : QListWidget(parent), m_TimeStep(0), m_SelfCall(false), m_MultiWidget(nullptr) { QListWidget::setAlternatingRowColors(true); // logic QListWidget::setSelectionBehavior(QAbstractItemView::SelectRows); QListWidget::setSelectionMode(QAbstractItemView::SingleSelection); connect(this, SIGNAL(itemDoubleClicked(QListWidgetItem *)), this, SLOT(OnItemDoubleClicked(QListWidgetItem *))); connect(this, SIGNAL(currentRowChanged(int)), this, SLOT(OnCurrentRowChanged(int))); } QmitkPointListViewWidget::~QmitkPointListViewWidget() { this->SetPointSet(nullptr); // remove listener } void QmitkPointListViewWidget::SetPointSet(mitk::PointSet *pointSet) { - if (!m_PointSet.IsExpired()) - { - auto pointSet = m_PointSet.Lock(); + auto lockedPointSet = m_PointSet.Lock(); - pointSet->RemoveObserver(m_PointSetModifiedTag); - pointSet->RemoveObserver(m_PointSetDeletedTag); + if (lockedPointSet.IsNotNull()) + { + lockedPointSet->RemoveObserver(m_PointSetModifiedTag); + lockedPointSet->RemoveObserver(m_PointSetDeletedTag); } m_PointSet = pointSet; + lockedPointSet = m_PointSet.Lock(); - if (!m_PointSet.IsExpired()) + if (lockedPointSet.IsNotNull()) { - auto pointSet = m_PointSet.Lock(); - auto onPointSetDeleted = itk::SimpleMemberCommand::New(); onPointSetDeleted->SetCallbackFunction(this, &QmitkPointListViewWidget::OnPointSetDeleted); - m_PointSetDeletedTag = pointSet->AddObserver(itk::DeleteEvent(), onPointSetDeleted); + m_PointSetDeletedTag = lockedPointSet->AddObserver(itk::DeleteEvent(), onPointSetDeleted); auto onPointSetModified = itk::SimpleMemberCommand::New(); onPointSetModified->SetCallbackFunction(this, &QmitkPointListViewWidget::OnPointSetChanged); - m_PointSetModifiedTag = pointSet->AddObserver(itk::DeleteEvent(), onPointSetModified); + m_PointSetModifiedTag = lockedPointSet->AddObserver(itk::DeleteEvent(), onPointSetModified); } this->Update(); } const mitk::PointSet *QmitkPointListViewWidget::GetPointSet() const { return m_PointSet.Lock(); } void QmitkPointListViewWidget::SetTimeStep(int t) { m_TimeStep = t; this->Update(); } int QmitkPointListViewWidget::GetTimeStep() const { return m_TimeStep; } void QmitkPointListViewWidget::SetMultiWidget(QmitkStdMultiWidget *multiWidget) { m_MultiWidget = multiWidget; } QmitkStdMultiWidget *QmitkPointListViewWidget::GetMultiWidget() const { return m_MultiWidget; } void QmitkPointListViewWidget::OnPointSetChanged() { if (!m_SelfCall) this->Update(); } void QmitkPointListViewWidget::OnPointSetDeleted() { this->SetPointSet(nullptr); this->Update(); } void QmitkPointListViewWidget::OnItemDoubleClicked(QListWidgetItem *item) { QmitkEditPointDialog _EditPointDialog(this); _EditPointDialog.SetPoint(m_PointSet.Lock(), this->row(item), m_TimeStep); _EditPointDialog.exec(); } void QmitkPointListViewWidget::OnCurrentRowChanged(int) { this->Update(true); } void QmitkPointListViewWidget::keyPressEvent(QKeyEvent *e) { if (m_PointSet.IsExpired()) return; int key = e->key(); switch (key) { case Qt::Key_F2: this->MoveSelectedPointUp(); break; case Qt::Key_F3: this->MoveSelectedPointDown(); break; case Qt::Key_Delete: this->RemoveSelectedPoint(); break; default: break; } } void QmitkPointListViewWidget::MoveSelectedPointUp() { - if (m_PointSet.IsExpired()) - return; - auto pointSet = m_PointSet.Lock(); + if (pointSet.IsNull()) + return; + mitk::PointSet::PointIdentifier selectedID; selectedID = pointSet->SearchSelectedPoint(m_TimeStep); mitk::PointOperation *doOp = new mitk::PointOperation(mitk::OpMOVEPOINTUP, pointSet->GetPoint(selectedID, m_TimeStep), selectedID, true); pointSet->ExecuteOperation(doOp); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); // Workaround for update problem in Pointset/Mapper } void QmitkPointListViewWidget::MoveSelectedPointDown() { - if (m_PointSet.IsExpired()) - return; - auto pointSet = m_PointSet.Lock(); + if (pointSet.IsNull()) + return; + mitk::PointSet::PointIdentifier selectedID; selectedID = pointSet->SearchSelectedPoint(m_TimeStep); mitk::PointOperation *doOp = new mitk::PointOperation(mitk::OpMOVEPOINTDOWN, pointSet->GetPoint(selectedID, m_TimeStep), selectedID, true); pointSet->ExecuteOperation(doOp); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); // Workaround for update problem in Pointset/Mapper } void QmitkPointListViewWidget::RemoveSelectedPoint() { - if (m_PointSet.IsExpired()) - return; - auto pointSet = m_PointSet.Lock(); + if (pointSet.IsNull()) + return; + mitk::PointSet::PointIdentifier selectedID; selectedID = pointSet->SearchSelectedPoint(m_TimeStep); mitk::PointOperation *doOp = new mitk::PointOperation(mitk::OpREMOVE, pointSet->GetPoint(selectedID, m_TimeStep), selectedID, true); pointSet->ExecuteOperation(doOp); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); // Workaround for update problem in Pointset/Mapper } void QmitkPointListViewWidget::Update(bool currentRowChanged) { if (m_SelfCall) return; - if (m_PointSet.IsExpired()) + auto pointSet = m_PointSet.Lock(); + + if (pointSet.IsNull()) { this->clear(); return; } - auto pointSet = m_PointSet.Lock(); - m_SelfCall = true; QString text; int i = 0; mitk::PointSet::DataType::Pointer pointset = pointSet->GetPointSet(m_TimeStep); for (mitk::PointSet::PointsContainer::Iterator it = pointset->GetPoints()->Begin(); it != pointset->GetPoints()->End(); ++it) { text = QString("%0: (%1, %2, %3)") .arg(i, 3) .arg(it.Value().GetElement(0), 0, 'f', 3) .arg(it.Value().GetElement(1), 0, 'f', 3) .arg(it.Value().GetElement(2), 0, 'f', 3); if (i == this->count()) this->addItem(text); // insert text else this->item(i)->setText(text); // update text if (currentRowChanged) { if (i == this->currentRow()) pointSet->SetSelectInfo(this->currentRow(), true, m_TimeStep); else pointSet->SetSelectInfo(it->Index(), false, m_TimeStep); // select nothing now } ++i; } // remove unnecessary listwidgetitems while (pointSet->GetPointSet(m_TimeStep)->GetPoints()->Size() < (unsigned int)this->count()) { QListWidgetItem *item = this->takeItem(this->count() - 1); delete item; } // update selection in pointset or in the list widget if (!currentRowChanged) { if (pointSet->GetNumberOfSelected(m_TimeStep) > 1) { /// @TODO use logging as soon as available std::cerr << "Point set has multiple selected points. This view is not designed for more than one selected point." << std::endl; } int selectedIndex = pointSet->SearchSelectedPoint(m_TimeStep); if (selectedIndex != -1) // no selected point is found { this->setCurrentRow(selectedIndex); } } m_SelfCall = false; } diff --git a/Modules/RenderWindowManagerUI/include/QmitkRenderWindowDataStorageListModel.h b/Modules/RenderWindowManagerUI/include/QmitkRenderWindowDataStorageListModel.h index 50b705f279..3e392fdf9b 100644 --- a/Modules/RenderWindowManagerUI/include/QmitkRenderWindowDataStorageListModel.h +++ b/Modules/RenderWindowManagerUI/include/QmitkRenderWindowDataStorageListModel.h @@ -1,104 +1,104 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #ifndef QMITKDATASTORAGERENDERWINDOWLISTMODEL_H #define QMITKDATASTORAGERENDERWINDOWLISTMODEL_H // render window manager UI model #include "MitkRenderWindowManagerUIExports.h" // render window manager module #include "mitkRenderWindowLayerController.h" #include "mitkRenderWindowLayerUtilities.h" //mitk core #include // qt widgets module #include /* * @brief The 'QmitkDataStorageRenderWindowListModel' is a list model derived from the 'QmitkAbstractDataStorageModel'. */ class MITKRENDERWINDOWMANAGERUI_EXPORT QmitkRenderWindowDataStorageListModel : public QmitkAbstractDataStorageModel { Q_OBJECT public: QmitkRenderWindowDataStorageListModel(QObject* parent = nullptr); // override from 'QmitkAbstractDataStorageModel' /** * @brief See 'QmitkAbstractDataStorageModel' */ void DataStorageChanged() override; /** * @brief See 'QmitkAbstractDataStorageModel' */ void NodePredicateChanged() override; /** * @brief See 'QmitkAbstractDataStorageModel' */ void NodeAdded(const mitk::DataNode* node) override; /** * @brief See 'QmitkAbstractDataStorageModel' */ void NodeChanged(const mitk::DataNode* node) override; /** * @brief See 'QmitkAbstractDataStorageModel' */ void NodeRemoved(const mitk::DataNode* node) override; // override from 'QAbstractItemModel' QModelIndex index(int row, int column, const QModelIndex& parent = QModelIndex()) const override; QModelIndex parent(const QModelIndex& child) const override; int rowCount(const QModelIndex& parent = QModelIndex()) const override; int columnCount(const QModelIndex& parent = QModelIndex()) const override; QVariant data(const QModelIndex& index, int role) const override; bool setData(const QModelIndex& index, const QVariant& value, int role = Qt::EditRole) override; Qt::ItemFlags flags(const QModelIndex& index) const override; Qt::DropActions supportedDropActions() const override; Qt::DropActions supportedDragActions() const override; QStringList mimeTypes() const override; QMimeData* mimeData(const QModelIndexList& indexes) const override; bool dropMimeData(const QMimeData* data, Qt::DropAction action, int row, int column, const QModelIndex& parent) override; void SetControlledRenderer(mitk::RenderWindowLayerUtilities::RendererVector controlledRenderer); void SetCurrentRenderer(mitk::BaseRenderer* baseRenderer); - mitk::BaseRenderer* GetCurrentRenderer() const; + mitk::BaseRenderer::Pointer GetCurrentRenderer() const; /** * @brief Use the RenderWindowLayerController to insert the given data node into all controlled render windows. * The new node is placed on top of all existing layer nodes in the render window. * * @param dataNode The data node that should be inserted. */ void AddDataNodeToAllRenderer(mitk::DataNode* dataNode); private: void UpdateModelData(); std::unique_ptr m_RenderWindowLayerController; mitk::WeakPointer m_BaseRenderer; mitk::RenderWindowLayerUtilities::LayerStack m_LayerStack; }; #endif // QMITKDATASTORAGERENDERWINDOWLISTMODEL_H diff --git a/Modules/RenderWindowManagerUI/include/QmitkRenderWindowDataStorageTreeModel.h b/Modules/RenderWindowManagerUI/include/QmitkRenderWindowDataStorageTreeModel.h index d1ce14312b..776e38304a 100644 --- a/Modules/RenderWindowManagerUI/include/QmitkRenderWindowDataStorageTreeModel.h +++ b/Modules/RenderWindowManagerUI/include/QmitkRenderWindowDataStorageTreeModel.h @@ -1,132 +1,132 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #ifndef QMITKDATASTORAGERENDERWINDOWTREEMODEL_H #define QMITKDATASTORAGERENDERWINDOWTREEMODEL_H // render window manager UI model #include "MitkRenderWindowManagerUIExports.h" // render window manager module #include "mitkRenderWindowLayerController.h" #include "mitkRenderWindowLayerUtilities.h" //mitk core #include // qt widgets module #include #include /* * @brief The 'QmitkRenderWindowDataStorageTreeModel' is a tree model derived from the 'QmitkAbstractDataStorageModel'. */ class MITKRENDERWINDOWMANAGERUI_EXPORT QmitkRenderWindowDataStorageTreeModel : public QmitkAbstractDataStorageModel { Q_OBJECT public: QmitkRenderWindowDataStorageTreeModel(QObject* parent = nullptr); // override from 'QmitkAbstractDataStorageModel' /** * @brief See 'QmitkAbstractDataStorageModel' */ void DataStorageChanged() override; /** * @brief See 'QmitkAbstractDataStorageModel' */ void NodePredicateChanged() override; /** * @brief See 'QmitkAbstractDataStorageModel' */ void NodeAdded(const mitk::DataNode* node) override; /** * @brief See 'QmitkAbstractDataStorageModel' */ void NodeChanged(const mitk::DataNode* node) override; /** * @brief See 'QmitkAbstractDataStorageModel' */ void NodeRemoved(const mitk::DataNode* node) override; // override from 'QAbstractItemModel' QModelIndex index(int row, int column, const QModelIndex& parent = QModelIndex()) const override; QModelIndex parent(const QModelIndex& parent) const override; int rowCount(const QModelIndex& parent = QModelIndex()) const override; int columnCount(const QModelIndex& parent = QModelIndex()) const override; QVariant data(const QModelIndex& index, int role) const override; bool setData(const QModelIndex& index, const QVariant& value, int role = Qt::EditRole) override; Qt::ItemFlags flags(const QModelIndex& index) const override; Qt::DropActions supportedDropActions() const override; Qt::DropActions supportedDragActions() const override; QStringList mimeTypes() const override; QMimeData* mimeData(const QModelIndexList& indexes) const override; bool dropMimeData(const QMimeData* data, Qt::DropAction action, int row, int column, const QModelIndex& parent) override; void SetControlledRenderer(mitk::RenderWindowLayerUtilities::RendererVector controlledRenderer); void SetCurrentRenderer(mitk::BaseRenderer* baseRenderer); - mitk::BaseRenderer* GetCurrentRenderer() const; + mitk::BaseRenderer::Pointer GetCurrentRenderer() const; private: void ResetTree(); void UpdateModelData(); /** * @brief Adjust the layer property according to the current tree. * The function will set the "layer" property of each underlying data node so that it fits the * the actual hierarchy represented by the current tree. */ void AdjustLayerProperty(); /** * @brief Fill a vector of tree items in a depth-first order (child-first). */ void TreeToVector(QmitkDataStorageTreeModelInternalItem* parent, std::vector& treeAsVector) const; /** * @brief Add the given data node to the tree of the given renderer. * The given renderer specifies the "layer"-property that is used for adding the new tree item * to the tree. The "layer"-property may be different for each renderer resulting in a * different tree for each renderer. * * @param dataNode The data node that should be added. * @param renderer The base renderer to which the data node should be added. */ void AddNodeInternal(const mitk::DataNode* dataNode, const mitk::BaseRenderer* renderer); /** * @brief Remove the tree item that contains the given data node. Removing an item may * leave the child items of the removed item without a parent. In this case * the children have to be moved inside the tree so the tree has to be rebuild * according to the current status of the data storage. * * @param dataNode The data node that should be removed. */ void RemoveNodeInternal(const mitk::DataNode* dataNode); mitk::DataNode* GetParentNode(const mitk::DataNode* node) const; QmitkDataStorageTreeModelInternalItem* GetItemByIndex(const QModelIndex& index) const; QModelIndex GetIndexByItem(QmitkDataStorageTreeModelInternalItem* item) const; std::unique_ptr m_RenderWindowLayerController; mitk::RenderWindowLayerUtilities::RendererVector m_ControlledRenderer; QmitkDataStorageTreeModelInternalItem* m_Root; mitk::WeakPointer m_BaseRenderer; }; #endif // QMITKDATASTORAGERENDERWINDOWTREEMODEL_H diff --git a/Modules/RenderWindowManagerUI/src/QmitkDataStorageLayerStackModel.cpp b/Modules/RenderWindowManagerUI/src/QmitkDataStorageLayerStackModel.cpp index 35f6733688..59232e7a5a 100644 --- a/Modules/RenderWindowManagerUI/src/QmitkDataStorageLayerStackModel.cpp +++ b/Modules/RenderWindowManagerUI/src/QmitkDataStorageLayerStackModel.cpp @@ -1,234 +1,234 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #include // qt widgets module #include "QmitkCustomVariants.h" #include "QmitkEnums.h" QmitkDataStorageLayerStackModel::QmitkDataStorageLayerStackModel(QObject* parent/* = nullptr*/) : QmitkAbstractDataStorageModel(parent) { // nothing here } void QmitkDataStorageLayerStackModel::DataStorageChanged() { UpdateModelData(); } void QmitkDataStorageLayerStackModel::NodePredicateChanged() { UpdateModelData(); } void QmitkDataStorageLayerStackModel::NodeAdded(const mitk::DataNode* /*node*/) { // nothing here; layers (nodes) are only added after button click } void QmitkDataStorageLayerStackModel::NodeChanged(const mitk::DataNode* /*node*/) { UpdateModelData(); } void QmitkDataStorageLayerStackModel::NodeRemoved(const mitk::DataNode* /*node*/) { UpdateModelData(); } void QmitkDataStorageLayerStackModel::SetCurrentRenderer(const std::string& renderWindowName) { if (!m_DataStorage.IsExpired()) { m_BaseRenderer = mitk::BaseRenderer::GetByName(renderWindowName); UpdateModelData(); } } mitk::BaseRenderer* QmitkDataStorageLayerStackModel::GetCurrentRenderer() const { return m_BaseRenderer.Lock(); } QModelIndex QmitkDataStorageLayerStackModel::index(int row, int column, const QModelIndex& parent) const { bool hasIndex = this->hasIndex(row, column, parent); if (hasIndex) { return this->createIndex(row, column); } return QModelIndex(); } QModelIndex QmitkDataStorageLayerStackModel::parent(const QModelIndex& /*child*/) const { return QModelIndex(); } Qt::ItemFlags QmitkDataStorageLayerStackModel::flags(const QModelIndex& index) const { Qt::ItemFlags flags = Qt::NoItemFlags; if (index.isValid() && index.model() == this) { flags = Qt::ItemIsEnabled | Qt::ItemIsSelectable; if (0 == index.column()) { flags |= Qt::ItemIsUserCheckable; } } return flags; } QVariant QmitkDataStorageLayerStackModel::headerData(int section, Qt::Orientation orientation, int role) const { if (Qt::Horizontal == orientation && Qt::DisplayRole == role) { if (0 == section) { return QVariant("Visibility"); } else if (1 == section) { return QVariant("Data node"); } } return QVariant(); } int QmitkDataStorageLayerStackModel::rowCount(const QModelIndex& parent/* = QModelIndex()*/) const { if (parent.isValid()) { return 0; } return static_cast(m_TempLayerStack.size()); } int QmitkDataStorageLayerStackModel::columnCount(const QModelIndex& parent/* = QModelIndex()*/) const { if (parent.isValid()) { return 0; } return 2; } QVariant QmitkDataStorageLayerStackModel::data(const QModelIndex& index, int role) const { if (!index.isValid() || index.model() != this) { return QVariant(); } if ((index.row()) < 0 || index.row() >= static_cast(m_TempLayerStack.size())) { return QVariant(); } RenderWindowLayerUtilities::LayerStack::const_iterator layerStackIt = m_TempLayerStack.begin(); std::advance(layerStackIt, index.row()); mitk::DataNode* dataNode = layerStackIt->second; if (Qt::CheckStateRole == role && 0 == index.column()) { bool visibility = false; dataNode->GetVisibility(visibility, m_BaseRenderer.Lock()); if (visibility) { return Qt::Checked; } else { return Qt::Unchecked; } } else if (Qt::DisplayRole == role && 1 == index.column()) { return QVariant(QString::fromStdString(dataNode->GetName())); } else if (Qt::ToolTipRole == role) { if (0 == index.column()) { return QVariant("Show/hide data node."); } else if (1 == index.column()) { return QVariant("Name of the data node."); } } else if (QmitkDataNodeRole == role) { return QVariant::fromValue(mitk::DataNode::Pointer(dataNode)); } else if (QmitkDataNodeRawPointerRole == role) { return QVariant::fromValue(dataNode); } return QVariant(); } bool QmitkDataStorageLayerStackModel::setData(const QModelIndex &index, const QVariant &value, int role /*= Qt::EditRole*/) { if (!index.isValid() || index.model() != this) { return false; } if ((index.row()) < 0 || index.row() >= static_cast(m_TempLayerStack.size())) { return false; } - if (!m_BaseRenderer.IsExpired()) - { - auto baseRenderer = m_BaseRenderer.Lock(); + auto baseRenderer = m_BaseRenderer.Lock(); + if (baseRenderer.IsNotNull()) + { RenderWindowLayerUtilities::LayerStack::const_iterator layerStackIt = m_TempLayerStack.begin(); std::advance(layerStackIt, index.row()); mitk::DataNode* dataNode = layerStackIt->second; if (Qt::CheckStateRole == role) { Qt::CheckState newCheckState = static_cast(value.toInt()); if (Qt::PartiallyChecked == newCheckState || Qt::Checked == newCheckState) { dataNode->SetVisibility(true, baseRenderer); } else { dataNode->SetVisibility(false, baseRenderer); } emit dataChanged(index, index); mitk::RenderingManager::GetInstance()->RequestUpdate(baseRenderer->GetRenderWindow()); return true; } } return false; } void QmitkDataStorageLayerStackModel::UpdateModelData() { // update the model, so that the table will be filled with the nodes according to the current // data storage and base renderer beginResetModel(); // get the current layer stack of the given base renderer m_TempLayerStack = RenderWindowLayerUtilities::GetLayerStack(m_DataStorage.Lock(), m_BaseRenderer.Lock(), true); endResetModel(); } diff --git a/Modules/RenderWindowManagerUI/src/QmitkRenderWindowDataStorageInspector.cpp b/Modules/RenderWindowManagerUI/src/QmitkRenderWindowDataStorageInspector.cpp index 29fdf72bb8..0d466173d2 100644 --- a/Modules/RenderWindowManagerUI/src/QmitkRenderWindowDataStorageInspector.cpp +++ b/Modules/RenderWindowManagerUI/src/QmitkRenderWindowDataStorageInspector.cpp @@ -1,169 +1,167 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ // render window manager UI module #include "QmitkRenderWindowDataStorageInspector.h" #include "QmitkCustomVariants.h" // mitk core #include // qt #include QmitkRenderWindowDataStorageInspector::QmitkRenderWindowDataStorageInspector(QWidget* parent /*=nullptr*/) : QmitkAbstractDataStorageInspector(parent) { m_Controls.setupUi(this); // initialize the render window layer controller and the render window view direction controller m_RenderWindowLayerController = std::make_unique(); m_RenderWindowViewDirectionController = std::make_unique(); m_StorageModel = std::make_unique(this); m_Controls.renderWindowTreeView->setModel(m_StorageModel.get()); m_Controls.renderWindowTreeView->setHeaderHidden(true); m_Controls.renderWindowTreeView->setEditTriggers(QAbstractItemView::NoEditTriggers); m_Controls.renderWindowTreeView->setSelectionBehavior(QAbstractItemView::SelectRows); m_Controls.renderWindowTreeView->setSelectionMode(QAbstractItemView::ExtendedSelection); m_Controls.renderWindowTreeView->setAlternatingRowColors(true); m_Controls.renderWindowTreeView->setDragEnabled(true); m_Controls.renderWindowTreeView->setDropIndicatorShown(true); m_Controls.renderWindowTreeView->setAcceptDrops(true); m_Controls.renderWindowTreeView->setContextMenuPolicy(Qt::CustomContextMenu); SetUpConnections(); } QAbstractItemView* QmitkRenderWindowDataStorageInspector::GetView() { return m_Controls.renderWindowTreeView; } const QAbstractItemView* QmitkRenderWindowDataStorageInspector::GetView() const { return m_Controls.renderWindowTreeView; } void QmitkRenderWindowDataStorageInspector::SetSelectionMode(SelectionMode mode) { m_Controls.renderWindowTreeView->setSelectionMode(mode); } QmitkRenderWindowDataStorageInspector::SelectionMode QmitkRenderWindowDataStorageInspector::GetSelectionMode() const { return m_Controls.renderWindowTreeView->selectionMode(); } void QmitkRenderWindowDataStorageInspector::Initialize() { - if (m_DataStorage.IsExpired()) - { - return; - } - auto dataStorage = m_DataStorage.Lock(); + if (dataStorage.IsNull()) + return; + m_StorageModel->SetDataStorage(dataStorage); m_StorageModel->SetNodePredicate(m_NodePredicate); m_RenderWindowLayerController->SetDataStorage(dataStorage); m_RenderWindowViewDirectionController->SetDataStorage(dataStorage); m_Connector->SetView(m_Controls.renderWindowTreeView); } void QmitkRenderWindowDataStorageInspector::SetUpConnections() { connect(m_StorageModel.get(), &QAbstractItemModel::rowsInserted, this, &QmitkRenderWindowDataStorageInspector::ModelRowsInserted); connect(m_Controls.pushButtonSetAsBaseLayer, &QPushButton::clicked, this, &QmitkRenderWindowDataStorageInspector::SetAsBaseLayer); connect(m_Controls.pushButtonResetRenderer, &QPushButton::clicked, this, &QmitkRenderWindowDataStorageInspector::ResetRenderer); QSignalMapper* changeViewDirectionSignalMapper = new QSignalMapper(this); changeViewDirectionSignalMapper->setMapping(m_Controls.radioButtonAxial, QString("axial")); changeViewDirectionSignalMapper->setMapping(m_Controls.radioButtonCoronal, QString("coronal")); changeViewDirectionSignalMapper->setMapping(m_Controls.radioButtonSagittal, QString("sagittal")); changeViewDirectionSignalMapper->setMapping(m_Controls.radioButton3D, QString("3D")); connect(changeViewDirectionSignalMapper, static_cast(&QSignalMapper::mapped), this, &QmitkRenderWindowDataStorageInspector::ChangeViewDirection); connect(m_Controls.radioButtonAxial, &QPushButton::clicked, changeViewDirectionSignalMapper, static_cast(&QSignalMapper::map)); connect(m_Controls.radioButtonCoronal, &QPushButton::clicked, changeViewDirectionSignalMapper, static_cast(&QSignalMapper::map)); connect(m_Controls.radioButtonSagittal, &QPushButton::clicked, changeViewDirectionSignalMapper, static_cast(&QSignalMapper::map)); connect(m_Controls.radioButton3D, &QPushButton::clicked, changeViewDirectionSignalMapper, static_cast(&QSignalMapper::map)); } void QmitkRenderWindowDataStorageInspector::SetControlledRenderer(mitk::RenderWindowLayerUtilities::RendererVector controlledRenderer) { m_StorageModel->SetControlledRenderer(controlledRenderer); m_RenderWindowLayerController->SetControlledRenderer(controlledRenderer); m_RenderWindowViewDirectionController->SetControlledRenderer(controlledRenderer); } void QmitkRenderWindowDataStorageInspector::SetActiveRenderWindow(const QString& renderWindowId) { mitk::BaseRenderer* selectedRenderer = mitk::BaseRenderer::GetByName(renderWindowId.toStdString()); if (nullptr == selectedRenderer) { return; } m_StorageModel->SetCurrentRenderer(selectedRenderer); mitk::SliceNavigationController::ViewDirection viewDirection = selectedRenderer->GetSliceNavigationController()->GetDefaultViewDirection(); switch (viewDirection) { case mitk::SliceNavigationController::Axial: m_Controls.radioButtonAxial->setChecked(true); break; case mitk::SliceNavigationController::Frontal: m_Controls.radioButtonCoronal->setChecked(true); break; case mitk::SliceNavigationController::Sagittal: m_Controls.radioButtonSagittal->setChecked(true); break; default: break; } } void QmitkRenderWindowDataStorageInspector::ModelRowsInserted(const QModelIndex& parent, int /*start*/, int /*end*/) { m_Controls.renderWindowTreeView->setExpanded(parent, true); } void QmitkRenderWindowDataStorageInspector::SetAsBaseLayer() { QModelIndex selectedIndex = m_Controls.renderWindowTreeView->currentIndex(); if (selectedIndex.isValid()) { QVariant qvariantDataNode = m_StorageModel->data(selectedIndex, Qt::UserRole); if (qvariantDataNode.canConvert()) { mitk::DataNode* dataNode = qvariantDataNode.value(); m_RenderWindowLayerController->SetBaseDataNode(dataNode, m_StorageModel->GetCurrentRenderer()); m_Controls.renderWindowTreeView->clearSelection(); } } } void QmitkRenderWindowDataStorageInspector::ResetRenderer() { m_RenderWindowLayerController->ResetRenderer(true, m_StorageModel->GetCurrentRenderer()); m_Controls.renderWindowTreeView->clearSelection(); } void QmitkRenderWindowDataStorageInspector::ChangeViewDirection(const QString& viewDirection) { m_RenderWindowViewDirectionController->SetViewDirectionOfRenderer(viewDirection.toStdString(), m_StorageModel->GetCurrentRenderer()); } diff --git a/Modules/RenderWindowManagerUI/src/QmitkRenderWindowDataStorageListModel.cpp b/Modules/RenderWindowManagerUI/src/QmitkRenderWindowDataStorageListModel.cpp index 67c0c79336..9b6face241 100644 --- a/Modules/RenderWindowManagerUI/src/QmitkRenderWindowDataStorageListModel.cpp +++ b/Modules/RenderWindowManagerUI/src/QmitkRenderWindowDataStorageListModel.cpp @@ -1,354 +1,351 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ // render window manager UI module #include "QmitkRenderWindowDataStorageListModel.h" // qt widgets module #include "QmitkCustomVariants.h" #include "QmitkEnums.h" #include "QmitkMimeTypes.h" #include "QmitkNodeDescriptorManager.h" QmitkRenderWindowDataStorageListModel::QmitkRenderWindowDataStorageListModel(QObject* parent /*= nullptr*/) : QmitkAbstractDataStorageModel(parent) { m_RenderWindowLayerController = std::make_unique(); } void QmitkRenderWindowDataStorageListModel::DataStorageChanged() { m_RenderWindowLayerController->SetDataStorage(m_DataStorage.Lock()); UpdateModelData(); } void QmitkRenderWindowDataStorageListModel::NodePredicateChanged() { UpdateModelData(); } void QmitkRenderWindowDataStorageListModel::NodeAdded(const mitk::DataNode* node) { // add a node to each render window specific list (or to a global list initially) AddDataNodeToAllRenderer(const_cast(node)); UpdateModelData(); } void QmitkRenderWindowDataStorageListModel::NodeChanged(const mitk::DataNode* /*node*/) { // nothing here, since the "'NodeChanged'-event is currently sent far too often } void QmitkRenderWindowDataStorageListModel::NodeRemoved(const mitk::DataNode* /*node*/) { // update model data to create a new list without the removed data node UpdateModelData(); } QModelIndex QmitkRenderWindowDataStorageListModel::index(int row, int column, const QModelIndex& parent) const { bool hasIndex = this->hasIndex(row, column, parent); if (hasIndex) { return this->createIndex(row, column); } return QModelIndex(); } QModelIndex QmitkRenderWindowDataStorageListModel::parent(const QModelIndex& /*child*/) const { return QModelIndex(); } int QmitkRenderWindowDataStorageListModel::rowCount(const QModelIndex& parent /*= QModelIndex()*/) const { if (parent.isValid()) { return 0; } return static_cast(m_LayerStack.size()); } int QmitkRenderWindowDataStorageListModel::columnCount(const QModelIndex& parent /*= QModelIndex()*/) const { if (parent.isValid()) { return 0; } return 1; } QVariant QmitkRenderWindowDataStorageListModel::data(const QModelIndex& index, int role) const { - if (m_BaseRenderer.IsExpired()) + auto baseRenderer = m_BaseRenderer.Lock(); + + if (baseRenderer.IsNull()) { return QVariant(); } - auto baseRenderer = m_BaseRenderer.Lock(); - if (!index.isValid() || this != index.model()) { return QVariant(); } if (index.row() < 0 || index.row() >= static_cast(m_LayerStack.size())) { return QVariant(); } mitk::RenderWindowLayerUtilities::LayerStack::const_iterator layerStackIt = m_LayerStack.begin(); std::advance(layerStackIt, index.row()); mitk::DataNode* dataNode = layerStackIt->second; if (Qt::CheckStateRole == role) { bool visibility = false; dataNode->GetVisibility(visibility, baseRenderer); if (visibility) { return Qt::Checked; } else { return Qt::Unchecked; } } else if (Qt::DisplayRole == role) { return QVariant(QString::fromStdString(dataNode->GetName())); } else if (Qt::ToolTipRole == role) { return QVariant("Name of the data node."); } else if (Qt::DecorationRole == role) { QmitkNodeDescriptor* nodeDescriptor = QmitkNodeDescriptorManager::GetInstance()->GetDescriptor(dataNode); return nodeDescriptor->GetIcon(dataNode); } else if (Qt::UserRole == role || QmitkDataNodeRawPointerRole == role) { // user role always returns a reference to the data node, // which can be used to modify the data node in the data storage return QVariant::fromValue(dataNode); } else if (QmitkDataNodeRole == role) { return QVariant::fromValue(mitk::DataNode::Pointer(dataNode)); } return QVariant(); } bool QmitkRenderWindowDataStorageListModel::setData(const QModelIndex& index, const QVariant& value, int role /*= Qt::EditRole*/) { - if (m_BaseRenderer.IsExpired()) + auto baseRenderer = m_BaseRenderer.Lock(); + + if (baseRenderer.IsNull()) { return false; } - auto baseRenderer = m_BaseRenderer.Lock(); - if (!index.isValid() || this != index.model()) { return false; } if (index.row() < 0 || index.row() >= static_cast(m_LayerStack.size())) { return false; } mitk::RenderWindowLayerUtilities::LayerStack::const_iterator layerStackIt = m_LayerStack.begin(); std::advance(layerStackIt, index.row()); mitk::DataNode* dataNode = layerStackIt->second; if (Qt::CheckStateRole == role) { Qt::CheckState newCheckState = static_cast(value.toInt()); bool isVisible = newCheckState; dataNode->SetVisibility(isVisible, baseRenderer); emit dataChanged(index, index); mitk::RenderingManager::GetInstance()->RequestUpdate(baseRenderer->GetRenderWindow()); return true; } return false; } Qt::ItemFlags QmitkRenderWindowDataStorageListModel::flags(const QModelIndex &index) const { if (this != index.model()) { return Qt::NoItemFlags; } if (!index.isValid()) { return Qt::ItemIsDropEnabled; } return Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsUserCheckable | Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled; } Qt::DropActions QmitkRenderWindowDataStorageListModel::supportedDropActions() const { return Qt::CopyAction | Qt::MoveAction; } Qt::DropActions QmitkRenderWindowDataStorageListModel::supportedDragActions() const { return Qt::CopyAction | Qt::MoveAction; } QStringList QmitkRenderWindowDataStorageListModel::mimeTypes() const { QStringList types = QAbstractItemModel::mimeTypes(); types << QmitkMimeTypes::DataNodePtrs; return types; } QMimeData* QmitkRenderWindowDataStorageListModel::mimeData(const QModelIndexList& indexes) const { QMimeData* mimeData = new QMimeData(); QByteArray encodedData; QDataStream stream(&encodedData, QIODevice::WriteOnly); for (const auto& index : indexes) { if (index.isValid()) { auto dataNode = data(index, QmitkDataNodeRawPointerRole).value(); stream << reinterpret_cast(dataNode); } } mimeData->setData(QmitkMimeTypes::DataNodePtrs, encodedData); return mimeData; } bool QmitkRenderWindowDataStorageListModel::dropMimeData(const QMimeData* data, Qt::DropAction action, int /*row*/, int column, const QModelIndex& parent) { - if (m_BaseRenderer.IsExpired()) + auto baseRenderer = m_BaseRenderer.Lock(); + + if (baseRenderer.IsNull()) { return false; } - auto baseRenderer = m_BaseRenderer.Lock(); - if (action == Qt::IgnoreAction) { return true; } if (!data->hasFormat(QmitkMimeTypes::DataNodePtrs)) { return false; } if (column > 0) { return false; } if (parent.isValid()) { int layer = -1; auto dataNode = this->data(parent, QmitkDataNodeRawPointerRole).value(); if (nullptr != dataNode) { dataNode->GetIntProperty("layer", layer, baseRenderer); } auto dataNodeList = QmitkMimeTypes::ToDataNodePtrList(data); for (const auto& dataNode : qAsConst(dataNodeList)) { m_RenderWindowLayerController->MoveNodeToPosition(dataNode, layer, baseRenderer); } UpdateModelData(); return true; } return false; } void QmitkRenderWindowDataStorageListModel::SetControlledRenderer(mitk::RenderWindowLayerUtilities::RendererVector controlledRenderer) { m_RenderWindowLayerController->SetControlledRenderer(controlledRenderer); + auto dataStorage = m_DataStorage.Lock(); - if (!m_DataStorage.IsExpired()) + if (dataStorage.IsNotNull()) { - auto dataStorage = m_DataStorage.Lock(); mitk::DataStorage::SetOfObjects::ConstPointer allDataNodes = dataStorage->GetAll(); for (mitk::DataStorage::SetOfObjects::ConstIterator it = allDataNodes->Begin(); it != allDataNodes->End(); ++it) { mitk::DataNode::Pointer dataNode = it->Value(); if (dataNode.IsNull()) { continue; } AddDataNodeToAllRenderer(dataNode); } } } void QmitkRenderWindowDataStorageListModel::SetCurrentRenderer(mitk::BaseRenderer* baseRenderer) { if (m_BaseRenderer == baseRenderer) { return; } m_BaseRenderer = baseRenderer; if (!m_BaseRenderer.IsExpired()) { UpdateModelData(); } } -mitk::BaseRenderer* QmitkRenderWindowDataStorageListModel::GetCurrentRenderer() const +mitk::BaseRenderer::Pointer QmitkRenderWindowDataStorageListModel::GetCurrentRenderer() const { - if (m_BaseRenderer.IsExpired()) - { - return nullptr; - } - - return m_BaseRenderer.Lock().GetPointer(); + return m_BaseRenderer.Lock(); } void QmitkRenderWindowDataStorageListModel::AddDataNodeToAllRenderer(mitk::DataNode* dataNode) { m_RenderWindowLayerController->InsertLayerNode(dataNode); } void QmitkRenderWindowDataStorageListModel::UpdateModelData() { - if (!m_DataStorage.IsExpired()) + auto dataStorage = m_DataStorage.Lock(); + + if (dataStorage.IsNotNull()) { - auto dataStorage = m_DataStorage.Lock(); - if (!m_BaseRenderer.IsExpired()) + auto baseRenderer = m_BaseRenderer.Lock(); + + if (baseRenderer.IsNotNull()) { - auto baseRenderer = m_BaseRenderer.Lock(); // update the model, so that it will be filled with the nodes of the new data storage beginResetModel(); // get the current layer stack of the given base renderer m_LayerStack = mitk::RenderWindowLayerUtilities::GetLayerStack(dataStorage, baseRenderer, true); endResetModel(); } } } diff --git a/Modules/RenderWindowManagerUI/src/QmitkRenderWindowDataStorageTreeModel.cpp b/Modules/RenderWindowManagerUI/src/QmitkRenderWindowDataStorageTreeModel.cpp index 8274403471..70a3d1c434 100644 --- a/Modules/RenderWindowManagerUI/src/QmitkRenderWindowDataStorageTreeModel.cpp +++ b/Modules/RenderWindowManagerUI/src/QmitkRenderWindowDataStorageTreeModel.cpp @@ -1,601 +1,600 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ // render window manager UI module #include "QmitkRenderWindowDataStorageTreeModel.h" // mitk core #include // qt widgets module #include "QmitkCustomVariants.h" #include "QmitkEnums.h" #include "QmitkMimeTypes.h" #include "QmitkNodeDescriptorManager.h" QmitkRenderWindowDataStorageTreeModel::QmitkRenderWindowDataStorageTreeModel(QObject* parent /*= nullptr*/) : QmitkAbstractDataStorageModel(parent) , m_Root(nullptr) { m_RenderWindowLayerController = std::make_unique(); ResetTree(); } void QmitkRenderWindowDataStorageTreeModel::DataStorageChanged() { m_RenderWindowLayerController->SetDataStorage(m_DataStorage.Lock()); ResetTree(); UpdateModelData(); } void QmitkRenderWindowDataStorageTreeModel::NodePredicateChanged() { ResetTree(); UpdateModelData(); } void QmitkRenderWindowDataStorageTreeModel::NodeAdded(const mitk::DataNode* node) { for (const auto renderer : m_ControlledRenderer) { // add the node to each render window mitk::RenderWindowLayerUtilities::SetRenderWindowProperties(const_cast(node), renderer); } - if (!m_BaseRenderer.IsExpired()) + auto baseRenderer = m_BaseRenderer.Lock(); + + if (baseRenderer.IsNotNull()) { - auto baseRenderer = m_BaseRenderer.Lock(); AddNodeInternal(node, baseRenderer); } } void QmitkRenderWindowDataStorageTreeModel::NodeChanged(const mitk::DataNode* node) { auto item = m_Root->Find(node); if (nullptr != item) { auto parentItem = item->GetParent(); // as the root node should not be removed one should always have a parent item if (nullptr == parentItem) { return; } auto index = createIndex(item->GetIndex(), 0, item); emit dataChanged(index, index); } } void QmitkRenderWindowDataStorageTreeModel::NodeRemoved(const mitk::DataNode* node) { RemoveNodeInternal(node); } QModelIndex QmitkRenderWindowDataStorageTreeModel::index(int row, int column, const QModelIndex& parent) const { auto item = GetItemByIndex(parent); if (nullptr != item) { item = item->GetChild(row); } if (nullptr == item) { return QModelIndex(); } return createIndex(row, column, item); } QModelIndex QmitkRenderWindowDataStorageTreeModel::parent(const QModelIndex& parent) const { auto item = GetItemByIndex(parent); if (nullptr != item) { item = item->GetParent(); } if(nullptr == item) { return QModelIndex(); } if (item == m_Root) { return QModelIndex(); } return createIndex(item->GetIndex(), 0, item); } int QmitkRenderWindowDataStorageTreeModel::rowCount(const QModelIndex& parent /*= QModelIndex()*/) const { auto item = GetItemByIndex(parent); if (nullptr == item) { return 0; } return item->GetChildCount(); } int QmitkRenderWindowDataStorageTreeModel::columnCount(const QModelIndex&/* parent = QModelIndex()*/) const { if (0 == m_Root->GetChildCount()) { // no items stored, no need to display columns return 0; } return 1; } QVariant QmitkRenderWindowDataStorageTreeModel::data(const QModelIndex& index, int role) const { - if (m_BaseRenderer.IsExpired()) + auto baseRenderer = m_BaseRenderer.Lock(); + + if (baseRenderer.IsNull()) { return QVariant(); } - auto baseRenderer = m_BaseRenderer.Lock(); - if (!index.isValid() || this != index.model()) { return QVariant(); } auto item = GetItemByIndex(index); if (nullptr == item) { return QVariant(); } auto dataNode = item->GetDataNode(); if (nullptr == dataNode) { return QVariant(); } if (Qt::CheckStateRole == role) { bool visibility = false; dataNode->GetVisibility(visibility, baseRenderer); if (visibility) { return Qt::Checked; } else { return Qt::Unchecked; } } else if (Qt::DisplayRole == role) { return QVariant(QString::fromStdString(dataNode->GetName())); } else if (Qt::ToolTipRole == role) { return QVariant("Name of the data node."); } else if (Qt::DecorationRole == role) { QmitkNodeDescriptor* nodeDescriptor = QmitkNodeDescriptorManager::GetInstance()->GetDescriptor(dataNode); return nodeDescriptor->GetIcon(dataNode); } else if (Qt::UserRole == role || QmitkDataNodeRawPointerRole == role) { // user role always returns a reference to the data node, // which can be used to modify the data node in the data storage return QVariant::fromValue(dataNode); } else if (QmitkDataNodeRole == role) { return QVariant::fromValue(mitk::DataNode::Pointer(dataNode)); } return QVariant(); } bool QmitkRenderWindowDataStorageTreeModel::setData(const QModelIndex& index, const QVariant& value, int role /*= Qt::EditRole*/) { - if (m_BaseRenderer.IsExpired()) + auto baseRenderer = m_BaseRenderer.Lock(); + + if (baseRenderer.IsNull()) { return false; } - auto baseRenderer = m_BaseRenderer.Lock(); - if (!index.isValid() || this != index.model()) { return false; } auto item = GetItemByIndex(index); if (nullptr == item) { return false; } auto dataNode = item->GetDataNode(); if (nullptr == dataNode) { return false; } if (Qt::EditRole == role && !value.toString().isEmpty()) { dataNode->SetName(value.toString().toStdString().c_str()); emit dataChanged(index, index); return true; } if (Qt::CheckStateRole == role) { Qt::CheckState newCheckState = static_cast(value.toInt()); bool isVisible = newCheckState; dataNode->SetVisibility(isVisible, baseRenderer); emit dataChanged(index, index); mitk::RenderingManager::GetInstance()->RequestUpdate(baseRenderer->GetRenderWindow()); return true; } return false; } Qt::ItemFlags QmitkRenderWindowDataStorageTreeModel::flags(const QModelIndex& index) const { if (this != index.model()) { return Qt::NoItemFlags; } if (!index.isValid()) { return Qt::ItemIsDropEnabled; } auto item = GetItemByIndex(index); if (nullptr == item) { return Qt::NoItemFlags; } const auto dataNode = item->GetDataNode(); if (m_NodePredicate.IsNull() || m_NodePredicate->CheckNode(dataNode)) { return Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsUserCheckable | Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled; } return Qt::NoItemFlags; } Qt::DropActions QmitkRenderWindowDataStorageTreeModel::supportedDropActions() const { return Qt::CopyAction | Qt::MoveAction; } Qt::DropActions QmitkRenderWindowDataStorageTreeModel::supportedDragActions() const { return Qt::CopyAction | Qt::MoveAction; } QStringList QmitkRenderWindowDataStorageTreeModel::mimeTypes() const { QStringList types = QAbstractItemModel::mimeTypes(); types << QmitkMimeTypes::DataNodePtrs; return types; } QMimeData* QmitkRenderWindowDataStorageTreeModel::mimeData(const QModelIndexList& indexes) const { QMimeData* mimeData = new QMimeData(); QByteArray encodedData; QDataStream stream(&encodedData, QIODevice::WriteOnly); for (const auto& index : indexes) { if (index.isValid()) { auto dataNode = data(index, QmitkDataNodeRawPointerRole).value(); stream << reinterpret_cast(dataNode); } } mimeData->setData(QmitkMimeTypes::DataNodePtrs, encodedData); return mimeData; } bool QmitkRenderWindowDataStorageTreeModel::dropMimeData(const QMimeData* data, Qt::DropAction action, int /*row*/, int /*column*/, const QModelIndex& parent) { - if (m_BaseRenderer.IsExpired()) + auto baseRenderer = m_BaseRenderer.Lock(); + + if (baseRenderer.IsNull()) { return false; } - auto baseRenderer = m_BaseRenderer.Lock(); - if (action == Qt::IgnoreAction) { return true; } if (!data->hasFormat(QmitkMimeTypes::DataNodePtrs)) { return false; } if (!parent.isValid()) { return false; } int layer = -1; auto dataNode = this->data(parent, QmitkDataNodeRawPointerRole).value(); if (nullptr != dataNode) { dataNode->GetIntProperty("layer", layer, baseRenderer); } auto dataNodeList = QmitkMimeTypes::ToDataNodePtrList(data); for (const auto& dataNode : qAsConst(dataNodeList)) { m_RenderWindowLayerController->MoveNodeToPosition(dataNode, layer, baseRenderer); } ResetTree(); UpdateModelData(); AdjustLayerProperty(); return true; } void QmitkRenderWindowDataStorageTreeModel::SetControlledRenderer(mitk::RenderWindowLayerUtilities::RendererVector controlledRenderer) { m_RenderWindowLayerController->SetControlledRenderer(controlledRenderer); m_ControlledRenderer = controlledRenderer; ResetTree(); - if (m_DataStorage.IsExpired()) + auto dataStorage = m_DataStorage.Lock(); + + if (dataStorage.IsNull()) { return; } - auto dataStorage = m_DataStorage.Lock(); - for (const auto& renderer : controlledRenderer) { if (nullptr == renderer) { continue; } auto allDataNodes = dataStorage->GetAll(); for (const auto& dataNode : *allDataNodes) { // add the node to each render window mitk::RenderWindowLayerUtilities::SetRenderWindowProperties(dataNode, renderer); } } } void QmitkRenderWindowDataStorageTreeModel::SetCurrentRenderer(mitk::BaseRenderer* baseRenderer) { if (m_BaseRenderer == baseRenderer) { return; } // base renderer changed // reset tree to build a new renderer-specific item hierarchy m_BaseRenderer = baseRenderer; ResetTree(); UpdateModelData(); } -mitk::BaseRenderer* QmitkRenderWindowDataStorageTreeModel::GetCurrentRenderer() const +mitk::BaseRenderer::Pointer QmitkRenderWindowDataStorageTreeModel::GetCurrentRenderer() const { - if (m_BaseRenderer.IsExpired()) - { - return nullptr; - } - - return m_BaseRenderer.Lock().GetPointer(); + return m_BaseRenderer.Lock(); } void QmitkRenderWindowDataStorageTreeModel::ResetTree() { beginResetModel(); if (nullptr != m_Root) { m_Root->Delete(); } mitk::DataNode::Pointer rootDataNode = mitk::DataNode::New(); rootDataNode->SetName("Data Storage"); m_Root = new QmitkDataStorageTreeModelInternalItem(rootDataNode); endResetModel(); } void QmitkRenderWindowDataStorageTreeModel::UpdateModelData() { - if (!m_DataStorage.IsExpired()) + auto dataStorage = m_DataStorage.Lock(); + + if (dataStorage.IsNotNull()) { - auto dataStorage = m_DataStorage.Lock(); - if (!m_BaseRenderer.IsExpired()) - { - auto baseRenderer = m_BaseRenderer.Lock(); + auto baseRenderer = m_BaseRenderer.Lock(); + if (baseRenderer.IsNotNull()) + { mitk::NodePredicateAnd::Pointer combinedNodePredicate = mitk::RenderWindowLayerUtilities::GetRenderWindowPredicate(baseRenderer); auto filteredDataNodes = dataStorage->GetSubset(combinedNodePredicate); for (const auto& dataNode : *filteredDataNodes) { AddNodeInternal(dataNode, baseRenderer); } } } } void QmitkRenderWindowDataStorageTreeModel::AdjustLayerProperty() { - if (m_BaseRenderer.IsExpired()) + auto baseRenderer = m_BaseRenderer.Lock(); + + if (baseRenderer.IsNull()) { return; } - auto baseRenderer = m_BaseRenderer.Lock(); - std::vector treeAsVector; TreeToVector(m_Root, treeAsVector); int i = treeAsVector.size() - 1; for (auto it = treeAsVector.begin(); it != treeAsVector.end(); ++it) { auto dataNode = (*it)->GetDataNode(); dataNode->SetIntProperty("layer", i, baseRenderer); --i; } } void QmitkRenderWindowDataStorageTreeModel::TreeToVector(QmitkDataStorageTreeModelInternalItem* parent, std::vector& treeAsVector) const { QmitkDataStorageTreeModelInternalItem* item; for (int i = 0; i < parent->GetChildCount(); ++i) { item = parent->GetChild(i); TreeToVector(item, treeAsVector); treeAsVector.push_back(item); } } void QmitkRenderWindowDataStorageTreeModel::AddNodeInternal(const mitk::DataNode* dataNode, const mitk::BaseRenderer* renderer) { if (nullptr == dataNode || m_DataStorage.IsExpired() || nullptr != m_Root->Find(dataNode)) { return; } // find out if we have a root node auto parentItem = m_Root; QModelIndex index; auto parentDataNode = GetParentNode(dataNode); if (nullptr != parentDataNode) // no top level data node { parentItem = m_Root->Find(parentDataNode); if (nullptr == parentItem) { // parent node not contained in the tree; add it NodeAdded(parentDataNode); parentItem = m_Root->Find(parentDataNode); if (nullptr == parentItem) { // could not find and add the parent tree; abort return; } } // get the index of this parent with the help of the grand parent index = createIndex(parentItem->GetIndex(), 0, parentItem); } int firstRowWithASiblingBelow = 0; int nodeLayer = -1; dataNode->GetIntProperty("layer", nodeLayer, renderer); for (const auto& siblingItem : parentItem->GetChildren()) { int siblingLayer = -1; auto siblingNode = siblingItem->GetDataNode(); if (nullptr != siblingNode) { siblingNode->GetIntProperty("layer", siblingLayer, renderer); } if (nodeLayer > siblingLayer) { break; } ++firstRowWithASiblingBelow; } beginInsertRows(index, firstRowWithASiblingBelow, firstRowWithASiblingBelow); auto newNode = new QmitkDataStorageTreeModelInternalItem(const_cast(dataNode)); parentItem->InsertChild(newNode, firstRowWithASiblingBelow); endInsertRows(); } void QmitkRenderWindowDataStorageTreeModel::RemoveNodeInternal(const mitk::DataNode* dataNode) { if (nullptr == dataNode || nullptr == m_Root) { return; } auto item = m_Root->Find(dataNode); if (nullptr == item) { return; } auto parentItem = item->GetParent(); auto parentIndex = GetIndexByItem(parentItem); auto children = item->GetChildren(); beginRemoveRows(parentIndex, item->GetIndex(), item->GetIndex()); parentItem->RemoveChild(item); delete item; endRemoveRows(); if (!children.empty()) { // rebuild tree because children could not be at the top level ResetTree(); UpdateModelData(); } } mitk::DataNode* QmitkRenderWindowDataStorageTreeModel::GetParentNode(const mitk::DataNode* node) const { mitk::DataNode* dataNode = nullptr; - if (m_DataStorage.IsExpired()) + auto dataStorage = m_DataStorage.Lock(); + + if (dataStorage.IsNull()) { return dataNode; } - auto sources = m_DataStorage.Lock()->GetSources(node); + auto sources = dataStorage->GetSources(node); if (sources->empty()) { return dataNode; } return sources->front(); } QmitkDataStorageTreeModelInternalItem* QmitkRenderWindowDataStorageTreeModel::GetItemByIndex(const QModelIndex& index) const { if (index.isValid()) { return static_cast(index.internalPointer()); } return m_Root; } QModelIndex QmitkRenderWindowDataStorageTreeModel::GetIndexByItem(QmitkDataStorageTreeModelInternalItem* item) const { if (item == m_Root) { return QModelIndex(); } return createIndex(item->GetIndex(), 0, item); } diff --git a/Modules/Segmentation/Controllers/mitkToolManager.cpp b/Modules/Segmentation/Controllers/mitkToolManager.cpp index 4a71cac9a9..bf4b30c3bf 100644 --- a/Modules/Segmentation/Controllers/mitkToolManager.cpp +++ b/Modules/Segmentation/Controllers/mitkToolManager.cpp @@ -1,603 +1,596 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #include "mitkToolManager.h" #include "mitkToolManagerProvider.h" #include "mitkCoreObjectFactory.h" #include #include #include #include "mitkInteractionEventObserver.h" #include "mitkSegTool2D.h" #include "mitkRenderingManager.h" #include "mitkSliceNavigationController.h" #include "usGetModuleContext.h" #include "usModuleContext.h" mitk::ToolManager::ToolManager(DataStorage *storage) : m_ActiveTool(nullptr), m_ActiveToolID(-1), m_RegisteredClients(0), m_DataStorage(storage) { CoreObjectFactory::GetInstance(); // to make sure a CoreObjectFactory was instantiated (and in turn, possible tools // are registered) - bug 1029 this->InitializeTools(); } void mitk::ToolManager::EnsureTimeObservation() { if (nullptr != mitk::RenderingManager::GetInstance() && nullptr != mitk::RenderingManager::GetInstance()->GetTimeNavigationController()) { auto timeController = mitk::RenderingManager::GetInstance()->GetTimeNavigationController(); m_LastTimePoint = mitk::RenderingManager::GetInstance()->GetTimeNavigationController()->GetSelectedTimePoint(); auto currentTimeController = m_CurrentTimeNavigationController.Lock(); if (timeController != currentTimeController) { if (currentTimeController.IsNotNull()) { currentTimeController->RemoveObserver(m_TimePointObserverTag); } itk::MemberCommand::Pointer command = itk::MemberCommand::New(); command->SetCallbackFunction(this, &ToolManager::OnTimeChanged); command->SetCallbackFunction(this, &ToolManager::OnTimeChangedConst); m_CurrentTimeNavigationController = timeController; m_TimePointObserverTag = timeController->AddObserver(SliceNavigationController::GeometryTimeEvent(nullptr,0), command); } } } void mitk::ToolManager::StopTimeObservation() { auto currentTimeController = m_CurrentTimeNavigationController.Lock(); if (currentTimeController.IsNotNull()) { currentTimeController->RemoveObserver(m_TimePointObserverTag); m_CurrentTimeNavigationController = nullptr; m_TimePointObserverTag = 0; } } mitk::ToolManager::~ToolManager() { for (auto dataIter = m_WorkingData.begin(); dataIter != m_WorkingData.end(); ++dataIter) (*dataIter)->RemoveObserver(m_WorkingDataObserverTags[(*dataIter)]); if (this->GetDataStorage() != nullptr) this->GetDataStorage()->RemoveNodeEvent.RemoveListener( mitk::MessageDelegate1(this, &ToolManager::OnNodeRemoved)); if (m_ActiveTool) { m_ActiveTool->Deactivated(); m_ActiveToolRegistration.Unregister(); m_ActiveTool = nullptr; m_ActiveToolID = -1; // no tool active ActiveToolChanged.Send(); } for (auto observerTagMapIter = m_ReferenceDataObserverTags.begin(); observerTagMapIter != m_ReferenceDataObserverTags.end(); ++observerTagMapIter) { observerTagMapIter->first->RemoveObserver(observerTagMapIter->second); } this->StopTimeObservation(); } void mitk::ToolManager::InitializeTools() { // clear all previous tool pointers (tools may be still activated from another recently used plugin) if (m_ActiveTool) { m_ActiveTool->Deactivated(); m_ActiveToolRegistration.Unregister(); m_ActiveTool = nullptr; m_ActiveToolID = -1; // no tool active ActiveToolChanged.Send(); } m_Tools.clear(); // get a list of all known mitk::Tools std::list thingsThatClaimToBeATool = itk::ObjectFactoryBase::CreateAllInstance("mitkTool"); // remember these tools for (auto iter = thingsThatClaimToBeATool.begin(); iter != thingsThatClaimToBeATool.end(); ++iter) { if (auto *tool = dynamic_cast(iter->GetPointer())) { tool->InitializeStateMachine(); tool->SetToolManager(this); // important to call right after instantiation tool->ErrorMessage += MessageDelegate1(this, &ToolManager::OnToolErrorMessage); tool->GeneralMessage += MessageDelegate1(this, &ToolManager::OnGeneralToolMessage); m_Tools.push_back(tool); } } } void mitk::ToolManager::OnToolErrorMessage(std::string s) { this->ToolErrorMessage(s); } void mitk::ToolManager::OnGeneralToolMessage(std::string s) { this->GeneralToolMessage(s); } const mitk::ToolManager::ToolVectorTypeConst mitk::ToolManager::GetTools() { ToolVectorTypeConst resultList; for (auto iter = m_Tools.begin(); iter != m_Tools.end(); ++iter) { resultList.push_back(iter->GetPointer()); } return resultList; } mitk::Tool *mitk::ToolManager::GetToolById(int id) { try { return m_Tools.at(id); } catch (const std::exception &) { return nullptr; } } bool mitk::ToolManager::ActivateTool(int id) { const auto workingDataNode = this->GetWorkingData(0); const mitk::BaseData* workingData = nullptr; if (nullptr != workingDataNode) { workingData = workingDataNode->GetData(); } const auto referenceDataNode = this->GetReferenceData(0); const mitk::BaseData* referenceData = nullptr; if (nullptr != referenceDataNode) { referenceData = referenceDataNode->GetData(); } if (id != -1 && !this->GetToolById(id)->CanHandle(referenceData, workingData)) return false; if (this->GetDataStorage()) { this->GetDataStorage()->RemoveNodeEvent.AddListener( mitk::MessageDelegate1(this, &ToolManager::OnNodeRemoved)); } if (GetToolById(id) == m_ActiveTool) return true; // no change needed static int nextTool = -1; nextTool = id; static bool inActivateTool = false; if (inActivateTool) { return true; } inActivateTool = true; while (nextTool != m_ActiveToolID) { // Deactivate all other active tools to ensure a globally single active tool for (const auto& toolManager : ToolManagerProvider::GetInstance()->GetToolManagers()) { if (nullptr != toolManager.second->m_ActiveTool) { toolManager.second->m_ActiveTool->Deactivated(); toolManager.second->m_ActiveToolRegistration.Unregister(); // The active tool of *this* ToolManager is handled below this loop if (this != toolManager.second) { toolManager.second->m_ActiveTool = nullptr; toolManager.second->m_ActiveToolID = -1; toolManager.second->ActiveToolChanged.Send(); } } } m_ActiveTool = GetToolById(nextTool); m_ActiveToolID = m_ActiveTool ? nextTool : -1; // current ID if tool is valid, otherwise -1 ActiveToolChanged.Send(); if (m_ActiveTool) { this->EnsureTimeObservation(); if (m_RegisteredClients > 0) { m_ActiveTool->Activated(); m_ActiveToolRegistration = us::GetModuleContext()->RegisterService(m_ActiveTool, us::ServiceProperties()); } } } inActivateTool = false; return (m_ActiveTool != nullptr); } void mitk::ToolManager::SetReferenceData(DataVectorType data) { if (data != m_ReferenceData) { // remove observers from old nodes for (auto dataIter = m_ReferenceData.begin(); dataIter != m_ReferenceData.end(); ++dataIter) { auto searchIter = m_ReferenceDataObserverTags.find(*dataIter); if (searchIter != m_ReferenceDataObserverTags.end()) { (*dataIter)->RemoveObserver(searchIter->second); } } m_ReferenceData = data; // TODO tell active tool? // attach new observers m_ReferenceDataObserverTags.clear(); for (auto dataIter = m_ReferenceData.begin(); dataIter != m_ReferenceData.end(); ++dataIter) { itk::MemberCommand::Pointer command = itk::MemberCommand::New(); command->SetCallbackFunction(this, &ToolManager::OnOneOfTheReferenceDataDeleted); command->SetCallbackFunction(this, &ToolManager::OnOneOfTheReferenceDataDeletedConst); m_ReferenceDataObserverTags.insert( std::pair((*dataIter), (*dataIter)->AddObserver(itk::DeleteEvent(), command))); } ReferenceDataChanged.Send(); } } void mitk::ToolManager::OnOneOfTheReferenceDataDeletedConst(const itk::Object *caller, const itk::EventObject &e) { OnOneOfTheReferenceDataDeleted(const_cast(caller), e); } void mitk::ToolManager::OnOneOfTheReferenceDataDeleted(itk::Object *caller, const itk::EventObject &itkNotUsed(e)) { DataVectorType v; for (auto dataIter = m_ReferenceData.begin(); dataIter != m_ReferenceData.end(); ++dataIter) { if ((void *)(*dataIter) != (void *)caller) { v.push_back(*dataIter); } else { m_ReferenceDataObserverTags.erase(*dataIter); // no tag to remove anymore } } this->SetReferenceData(v); } void mitk::ToolManager::SetReferenceData(DataNode *data) { DataVectorType v; if (data) { v.push_back(data); } SetReferenceData(v); } void mitk::ToolManager::SetWorkingData(DataVectorType data) { if (data != m_WorkingData) { // remove observers from old nodes for (auto dataIter = m_WorkingData.begin(); dataIter != m_WorkingData.end(); ++dataIter) { auto searchIter = m_WorkingDataObserverTags.find(*dataIter); if (searchIter != m_WorkingDataObserverTags.end()) { (*dataIter)->RemoveObserver(searchIter->second); } } m_WorkingData = data; // TODO tell active tool? // Quick workaround for bug #16598 if (m_WorkingData.empty()) this->ActivateTool(-1); // workaround end // attach new observers m_WorkingDataObserverTags.clear(); for (auto dataIter = m_WorkingData.begin(); dataIter != m_WorkingData.end(); ++dataIter) { itk::MemberCommand::Pointer command = itk::MemberCommand::New(); command->SetCallbackFunction(this, &ToolManager::OnOneOfTheWorkingDataDeleted); command->SetCallbackFunction(this, &ToolManager::OnOneOfTheWorkingDataDeletedConst); m_WorkingDataObserverTags.insert( std::pair((*dataIter), (*dataIter)->AddObserver(itk::DeleteEvent(), command))); } WorkingDataChanged.Send(); } } void mitk::ToolManager::OnOneOfTheWorkingDataDeletedConst(const itk::Object *caller, const itk::EventObject &e) { OnOneOfTheWorkingDataDeleted(const_cast(caller), e); } void mitk::ToolManager::OnOneOfTheWorkingDataDeleted(itk::Object *caller, const itk::EventObject &itkNotUsed(e)) { DataVectorType v; for (auto dataIter = m_WorkingData.begin(); dataIter != m_WorkingData.end(); ++dataIter) { if ((void *)(*dataIter) != (void *)caller) { v.push_back(*dataIter); } else { m_WorkingDataObserverTags.erase(*dataIter); // no tag to remove anymore } } this->SetWorkingData(v); } void mitk::ToolManager::SetWorkingData(DataNode *data) { DataVectorType v; if (data) // don't allow for nullptr nodes { v.push_back(data); } SetWorkingData(v); } void mitk::ToolManager::SetRoiData(DataVectorType data) { if (data != m_RoiData) { // remove observers from old nodes for (auto dataIter = m_RoiData.begin(); dataIter != m_RoiData.end(); ++dataIter) { auto searchIter = m_RoiDataObserverTags.find(*dataIter); if (searchIter != m_RoiDataObserverTags.end()) { (*dataIter)->RemoveObserver(searchIter->second); } } m_RoiData = data; // TODO tell active tool? // attach new observers m_RoiDataObserverTags.clear(); for (auto dataIter = m_RoiData.begin(); dataIter != m_RoiData.end(); ++dataIter) { itk::MemberCommand::Pointer command = itk::MemberCommand::New(); command->SetCallbackFunction(this, &ToolManager::OnOneOfTheRoiDataDeleted); command->SetCallbackFunction(this, &ToolManager::OnOneOfTheRoiDataDeletedConst); m_RoiDataObserverTags.insert( std::pair((*dataIter), (*dataIter)->AddObserver(itk::DeleteEvent(), command))); } RoiDataChanged.Send(); } } void mitk::ToolManager::SetRoiData(DataNode *data) { DataVectorType v; if (data) { v.push_back(data); } this->SetRoiData(v); } void mitk::ToolManager::OnOneOfTheRoiDataDeletedConst(const itk::Object *caller, const itk::EventObject &e) { OnOneOfTheRoiDataDeleted(const_cast(caller), e); } void mitk::ToolManager::OnOneOfTheRoiDataDeleted(itk::Object *caller, const itk::EventObject &itkNotUsed(e)) { DataVectorType v; for (auto dataIter = m_RoiData.begin(); dataIter != m_RoiData.end(); ++dataIter) { if ((void *)(*dataIter) != (void *)caller) { v.push_back(*dataIter); } else { m_RoiDataObserverTags.erase(*dataIter); // no tag to remove anymore } } this->SetRoiData(v); } mitk::ToolManager::DataVectorType mitk::ToolManager::GetReferenceData() { return m_ReferenceData; } mitk::DataNode *mitk::ToolManager::GetReferenceData(int idx) { try { return m_ReferenceData.at(idx); } catch (const std::exception &) { return nullptr; } } mitk::ToolManager::DataVectorType mitk::ToolManager::GetWorkingData() { return m_WorkingData; } mitk::ToolManager::DataVectorType mitk::ToolManager::GetRoiData() { return m_RoiData; } mitk::DataNode *mitk::ToolManager::GetRoiData(int idx) { try { return m_RoiData.at(idx); } catch (const std::exception &) { return nullptr; } } -mitk::DataStorage *mitk::ToolManager::GetDataStorage() +mitk::DataStorage::Pointer mitk::ToolManager::GetDataStorage() const { - if (!m_DataStorage.IsExpired()) - { - return m_DataStorage.Lock(); - } - else - { - return nullptr; - } + return m_DataStorage.Lock(); } void mitk::ToolManager::SetDataStorage(DataStorage &storage) { m_DataStorage = &storage; } mitk::DataNode *mitk::ToolManager::GetWorkingData(unsigned int idx) { if (m_WorkingData.empty()) return nullptr; if (m_WorkingData.size() > idx) return m_WorkingData[idx]; return nullptr; } int mitk::ToolManager::GetActiveToolID() { return m_ActiveToolID; } mitk::Tool *mitk::ToolManager::GetActiveTool() { return m_ActiveTool; } void mitk::ToolManager::RegisterClient() { if (m_RegisteredClients < 1) { if (m_ActiveTool) { m_ActiveTool->Activated(); m_ActiveToolRegistration = us::GetModuleContext()->RegisterService(m_ActiveTool, us::ServiceProperties()); } } ++m_RegisteredClients; } void mitk::ToolManager::UnregisterClient() { if (m_RegisteredClients < 1) return; --m_RegisteredClients; if (m_RegisteredClients < 1) { if (m_ActiveTool) { m_ActiveTool->Deactivated(); m_ActiveToolRegistration.Unregister(); } } } int mitk::ToolManager::GetToolID(const Tool *tool) { int id(0); for (auto iter = m_Tools.begin(); iter != m_Tools.end(); ++iter, ++id) { if (tool == iter->GetPointer()) { return id; } } return -1; } void mitk::ToolManager::OnNodeRemoved(const mitk::DataNode *node) { // check all storage vectors OnOneOfTheReferenceDataDeleted(const_cast(node), itk::DeleteEvent()); OnOneOfTheRoiDataDeleted(const_cast(node), itk::DeleteEvent()); OnOneOfTheWorkingDataDeleted(const_cast(node), itk::DeleteEvent()); } void mitk::ToolManager::OnTimeChanged(itk::Object* caller, const itk::EventObject& e) { this->OnTimeChangedConst(caller, e); } void mitk::ToolManager::OnTimeChangedConst(const itk::Object* caller, const itk::EventObject& /*e*/) { auto currentController = m_CurrentTimeNavigationController.Lock(); if (caller == currentController) { const auto currentTimePoint = currentController->GetSelectedTimePoint(); if (currentTimePoint != m_LastTimePoint) { m_LastTimePoint = currentTimePoint; SelectedTimePointChanged.Send(); } } } mitk::TimePointType mitk::ToolManager::GetCurrentTimePoint() const { return m_LastTimePoint; } diff --git a/Modules/Segmentation/Controllers/mitkToolManager.h b/Modules/Segmentation/Controllers/mitkToolManager.h index 4cdfbf263a..9079e2579d 100644 --- a/Modules/Segmentation/Controllers/mitkToolManager.h +++ b/Modules/Segmentation/Controllers/mitkToolManager.h @@ -1,302 +1,302 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #ifndef mitkToolManager_h_Included #define mitkToolManager_h_Included #include "mitkDataNode.h" #include "mitkDataStorage.h" #include "mitkTool.h" #include "mitkWeakPointer.h" #include #pragma GCC visibility push(default) #include #pragma GCC visibility pop #include namespace mitk { class Image; class PlaneGeometry; /** \brief Manages and coordinates instances of mitk::Tool. \sa QmitkToolSelectionBox \sa Tool \sa QmitkSegmentationView \ingroup Interaction \ingroup ToolManagerEtAl There is a separate page describing the general design of QmitkSegmentationView: \ref QmitkSegmentationTechnicalPage This class creates and manages several instances of mitk::Tool. \li ToolManager creates instances of mitk::Tool by asking the itk::ObjectFactory to list all known implementations of mitk::Tool. As a result, one has to implement both a subclass of mitk::Tool and a matching subclass of itk::ObjectFactoryBase that is registered to the top-level itk::ObjectFactory. For an example, see mitkContourToolFactory.h. (this limitiation of one-class-one-factory is due to the implementation of itk::ObjectFactory). In MITK, the right place to register the factories to itk::ObjectFactory is the mitk::QMCoreObjectFactory or mitk::SBCoreObjectFactory. \li ToolManager knows a set of "reference" DataNodes and a set of "working" DataNodes. The first application are segmentation tools, where the reference is the original image and the working data the (kind of) binary segmentation. However, ToolManager is implemented more generally, so that there could be other tools that work, e.g., with surfaces. \li There is a set of events that are sent by ToolManager. At the moment these are TODO update documentation: - mitk::ToolReferenceDataChangedEvent whenever somebody calls SetReferenceData. Most of the time this actually means that the data has changed, but there might be cases where the same data is passed to SetReferenceData a second time, so don't rely on the assumption that something actually changed. - mitk::ToolSelectedEvent is sent when a (truly) different tool was activated. In reaction to this event you can ask for the active Tool using GetActiveTool or GetActiveToolID (where nullptr or -1 indicate that NO tool is active at the moment). Design descisions: \li Not a singleton, because there could be two functionalities using tools, each one with different reference/working data. $Author$ */ class MITKSEGMENTATION_EXPORT ToolManager : public itk::Object { public: typedef std::vector ToolVectorType; typedef std::vector ToolVectorTypeConst; typedef std::vector DataVectorType; // has to be observed for delete events! typedef std::map NodeTagMapType; Message<> NodePropertiesChanged; Message<> NewNodesGenerated; Message1 NewNodeObjectsGenerated; Message<> ActiveToolChanged; Message<> ReferenceDataChanged; Message<> WorkingDataChanged; Message<> RoiDataChanged; Message<> SelectedTimePointChanged; Message1 ToolErrorMessage; Message1 GeneralToolMessage; mitkClassMacroItkParent(ToolManager, itk::Object); mitkNewMacro1Param(ToolManager, DataStorage *); /** \brief Gives you a list of all tools. This is const on purpose. */ const ToolVectorTypeConst GetTools(); int GetToolID(const Tool *tool); /** \param id The tool of interest. Counting starts with 0. */ Tool *GetToolById(int id); /** \param id The tool to activate. Provide -1 for disabling any tools. Counting starts with 0. Registeres a listner for NodeRemoved event at DataStorage (see mitk::ToolManager::OnNodeRemoved). */ bool ActivateTool(int id); template int GetToolIdByToolType() { int id = 0; for (auto iter = m_Tools.begin(); iter != m_Tools.end(); ++iter, ++id) { if (dynamic_cast(iter->GetPointer())) { return id; } } return -1; } /** \return -1 for "No tool is active" */ int GetActiveToolID(); /** \return nullptr for "No tool is active" */ Tool *GetActiveTool(); /** \brief Set a list of data/images as reference objects. */ void SetReferenceData(DataVectorType); /** \brief Set single data item/image as reference object. */ void SetReferenceData(DataNode *); /** \brief Set a list of data/images as working objects. */ void SetWorkingData(DataVectorType); /** \brief Set single data item/image as working object. */ void SetWorkingData(DataNode *); /** \brief Set a list of data/images as roi objects. */ void SetRoiData(DataVectorType); /** \brief Set a single data item/image as roi object. */ void SetRoiData(DataNode *); /** \brief Get the list of reference data. */ DataVectorType GetReferenceData(); /** \brief Get the current reference data. \warning If there is a list of items, this method will only return the first list item. */ DataNode *GetReferenceData(int); /** \brief Get the list of working data. */ DataVectorType GetWorkingData(); /** \brief Get the current working data. \warning If there is a list of items, this method will only return the first list item. */ DataNode *GetWorkingData(unsigned int); /** \brief Get the current roi data */ DataVectorType GetRoiData(); /** \brief Get the roi data at position idx */ DataNode *GetRoiData(int idx); - DataStorage *GetDataStorage(); + DataStorage::Pointer GetDataStorage() const; void SetDataStorage(DataStorage &storage); /** Get the current selected time point of the RenderManager */ TimePointType GetCurrentTimePoint() const; /** \brief Tell that someone is using tools. GUI elements should call this when they become active. This method increases an internal "client count". */ void RegisterClient(); /** \brief Tell that someone is NOT using tools. GUI elements should call this when they become active. This method increases an internal "client count". */ void UnregisterClient(); /** \brief Initialize all classes derived from mitk::Tool by itkObjectFactoy */ void InitializeTools(); void OnOneOfTheReferenceDataDeletedConst(const itk::Object *caller, const itk::EventObject &e); void OnOneOfTheReferenceDataDeleted(itk::Object *caller, const itk::EventObject &e); void OnOneOfTheWorkingDataDeletedConst(const itk::Object *caller, const itk::EventObject &e); void OnOneOfTheWorkingDataDeleted(itk::Object *caller, const itk::EventObject &e); void OnOneOfTheRoiDataDeletedConst(const itk::Object *caller, const itk::EventObject &e); void OnOneOfTheRoiDataDeleted(itk::Object *caller, const itk::EventObject &e); /** \brief Connected to tool's messages This method just resends error messages coming from any of the tools. This way clients (GUIs) only have to observe one message. */ void OnToolErrorMessage(std::string s); void OnGeneralToolMessage(std::string s); protected: /** You may specify a list of tool "groups" that should be available for this ToolManager. Every Tool can report its group as a string. This constructor will try to find the tool's group inside the supplied string. If there is a match, the tool is accepted. Effectively, you can provide a human readable list like "default, lymphnodevolumetry, oldERISstuff". */ ToolManager(DataStorage *storage); // purposely hidden ~ToolManager() override; ToolVectorType m_Tools; Tool *m_ActiveTool; int m_ActiveToolID; us::ServiceRegistration m_ActiveToolRegistration; DataVectorType m_ReferenceData; NodeTagMapType m_ReferenceDataObserverTags; DataVectorType m_WorkingData; NodeTagMapType m_WorkingDataObserverTags; DataVectorType m_RoiData; NodeTagMapType m_RoiDataObserverTags; int m_RegisteredClients; WeakPointer m_DataStorage; /// \brief Callback for NodeRemove events void OnNodeRemoved(const mitk::DataNode *node); /** Callback for time changed events*/ void OnTimeChangedConst(const itk::Object* caller, const itk::EventObject& e); void OnTimeChanged(itk::Object* caller, const itk::EventObject& e); void EnsureTimeObservation(); void StopTimeObservation(); private: /** Time point of last detected change*/ TimePointType m_LastTimePoint = 0; /** Tag of the observer that listens to time changes*/ unsigned long m_TimePointObserverTag = 0; /** Pointer to the observed time stepper*/ WeakPointer m_CurrentTimeNavigationController; }; } // namespace #endif diff --git a/Modules/SemanticRelations/src/mitkSemanticRelationsDataStorageAccess.cpp b/Modules/SemanticRelations/src/mitkSemanticRelationsDataStorageAccess.cpp index c2712ece4f..5994a9a4d7 100644 --- a/Modules/SemanticRelations/src/mitkSemanticRelationsDataStorageAccess.cpp +++ b/Modules/SemanticRelations/src/mitkSemanticRelationsDataStorageAccess.cpp @@ -1,338 +1,348 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #include "mitkSemanticRelationsDataStorageAccess.h" // semantic relations module #include "mitkControlPointManager.h" #include "mitkDICOMHelper.h" #include "mitkNodePredicates.h" #include "mitkRelationStorage.h" #include "mitkSemanticRelationException.h" #include "mitkSemanticRelationsInference.h" // c++ #include #include mitk::SemanticRelationsDataStorageAccess::SemanticRelationsDataStorageAccess(DataStorage* dataStorage) : m_DataStorage(dataStorage) { // nothing here } /************************************************************************/ /* functions to get instances / attributes */ /************************************************************************/ mitk::SemanticRelationsDataStorageAccess::DataNodeVector mitk::SemanticRelationsDataStorageAccess::GetAllSegmentationsOfCase(const SemanticTypes::CaseID& caseID) const { - if (m_DataStorage.IsExpired()) + auto dataStorage = m_DataStorage.Lock(); + + if (dataStorage.IsNull()) { mitkThrowException(SemanticRelationException) << "Not a valid data storage."; } SemanticTypes::IDVector allSegmentationIDsOfCase = RelationStorage::GetAllSegmentationIDsOfCase(caseID); DataNodeVector allSegmentationsOfCase; // get all segmentation nodes of the current data storage // only those nodes are respected, that are currently held in the data storage - DataStorage::SetOfObjects::ConstPointer segmentationNodes = m_DataStorage.Lock()->GetSubset(NodePredicates::GetSegmentationPredicate()); + DataStorage::SetOfObjects::ConstPointer segmentationNodes = dataStorage->GetSubset(NodePredicates::GetSegmentationPredicate()); for (auto it = segmentationNodes->Begin(); it != segmentationNodes->End(); ++it) { DataNode* segmentationNode = it->Value(); SemanticTypes::CaseID currentCaseID; SemanticTypes::ID segmentationID; try { // find the corresponding segmentation node for the given segmentation ID currentCaseID = GetCaseIDFromDataNode(segmentationNode); segmentationID = GetIDFromDataNode(segmentationNode); } catch (SemanticRelationException&) { // found a segmentation node that is not stored in the semantic relations // this segmentation node does not have any DICOM information --> exception thrown // continue with the next segmentation to compare IDs continue; } if (caseID == currentCaseID && (std::find(allSegmentationIDsOfCase.begin(), allSegmentationIDsOfCase.end(), segmentationID) != allSegmentationIDsOfCase.end())) { // found current image node in the storage, add it to the return vector allSegmentationsOfCase.push_back(segmentationNode); } } return allSegmentationsOfCase; } mitk::SemanticRelationsDataStorageAccess::DataNodeVector mitk::SemanticRelationsDataStorageAccess::GetAllSegmentationsOfLesion(const SemanticTypes::CaseID& caseID, const SemanticTypes::Lesion& lesion) const { if (SemanticRelationsInference::InstanceExists(caseID, lesion)) { // lesion exists, retrieve all case segmentations from the storage DataNodeVector allSegmentationsOfLesion = GetAllSegmentationsOfCase(caseID); // filter all segmentations: check for semantic relation with the given lesion using a lambda function auto lambda = [&lesion](DataNode::Pointer segmentation) { try { SemanticTypes::Lesion representedLesion = SemanticRelationsInference::GetLesionOfSegmentation(segmentation); return lesion.UID != representedLesion.UID; } catch (const SemanticRelationException&) { return true; } }; allSegmentationsOfLesion.erase(std::remove_if(allSegmentationsOfLesion.begin(), allSegmentationsOfLesion.end(), lambda), allSegmentationsOfLesion.end()); return allSegmentationsOfLesion; } else { mitkThrowException(SemanticRelationException) << "Could not find an existing lesion instance for the given caseID " << caseID << " and lesion " << lesion.UID << "."; } } mitk::SemanticRelationsDataStorageAccess::DataNodeVector mitk::SemanticRelationsDataStorageAccess::GetAllImagesOfCase(const SemanticTypes::CaseID& caseID) const { - if (m_DataStorage.IsExpired()) + auto dataStorage = m_DataStorage.Lock(); + + if (dataStorage.IsNull()) { mitkThrowException(SemanticRelationException) << "Not a valid data storage."; } SemanticTypes::IDVector allImageIDsOfCase = RelationStorage::GetAllImageIDsOfCase(caseID); DataNodeVector allImagesOfCase; // get all image nodes of the current data storage // only those nodes are respected, that are currently held in the data storage - DataStorage::SetOfObjects::ConstPointer imageNodes = m_DataStorage.Lock()->GetSubset(NodePredicates::GetImagePredicate()); + DataStorage::SetOfObjects::ConstPointer imageNodes = dataStorage->GetSubset(NodePredicates::GetImagePredicate()); for (auto it = imageNodes->Begin(); it != imageNodes->End(); ++it) { DataNode* imageNode = it->Value(); SemanticTypes::CaseID currentCaseID; SemanticTypes::ID imageID; try { // find the corresponding image node for the given segmentation ID currentCaseID = GetCaseIDFromDataNode(imageNode); imageID = GetIDFromDataNode(imageNode); } catch (SemanticRelationException&) { // found an image node that is not stored in the semantic relations // this image node does not have any DICOM information --> exception thrown // continue with the next image to compare IDs continue; } if (caseID == currentCaseID && (std::find(allImageIDsOfCase.begin(), allImageIDsOfCase.end(), imageID) != allImageIDsOfCase.end())) { // found current image node in the storage, add it to the return vector allImagesOfCase.push_back(imageNode); } } return allImagesOfCase; } mitk::SemanticRelationsDataStorageAccess::DataNodeVector mitk::SemanticRelationsDataStorageAccess::GetAllImagesByID(const SemanticTypes::IDVector& imageIDs) const { - if (m_DataStorage.IsExpired()) + auto dataStorage = m_DataStorage.Lock(); + + if (dataStorage.IsNull()) { mitkThrowException(SemanticRelationException) << "Not a valid data storage."; } DataNodeVector allImagesOfCase; // get all image nodes of the current data storage // only those nodes are respected, that are currently held in the data storage - DataStorage::SetOfObjects::ConstPointer imageNodes = m_DataStorage.Lock()->GetSubset(NodePredicates::GetImagePredicate()); + DataStorage::SetOfObjects::ConstPointer imageNodes = dataStorage->GetSubset(NodePredicates::GetImagePredicate()); for (auto it = imageNodes->Begin(); it != imageNodes->End(); ++it) { DataNode* imageNode = it->Value(); SemanticTypes::CaseID currentCaseID; SemanticTypes::ID imageID; try { // find the corresponding image node for the given segmentation ID imageID = GetIDFromDataNode(imageNode); } catch (SemanticRelationException&) { // found an image node that is not stored in the semantic relations // this image node does not have any DICOM information --> exception thrown // continue with the next image to compare IDs continue; } if (std::find(imageIDs.begin(), imageIDs.end(), imageID) != imageIDs.end()) { // found current image node in the storage, add it to the return vector allImagesOfCase.push_back(imageNode); } } return allImagesOfCase; } mitk::SemanticRelationsDataStorageAccess::DataNodeVector mitk::SemanticRelationsDataStorageAccess::GetAllImagesOfLesion(const SemanticTypes::CaseID& caseID, const SemanticTypes::Lesion& lesion) const { - if (m_DataStorage.IsExpired()) + auto dataStorage = m_DataStorage.Lock(); + + if (dataStorage.IsNull()) { mitkThrowException(SemanticRelationException) << "Not a valid data storage."; } DataNodeVector allImagesOfLesion; // 1. get all segmentations that define the lesion // 2. retrieve the parent node (source) of the found segmentation node DataNodeVector allSegmentationsOfLesion = GetAllSegmentationsOfLesion(caseID, lesion); for (const auto& segmentationNode : allSegmentationsOfLesion) { // get parent node of the current segmentation node with the node predicate - DataStorage::SetOfObjects::ConstPointer parentNodes = m_DataStorage.Lock()->GetSources(segmentationNode, NodePredicates::GetImagePredicate(), false); + DataStorage::SetOfObjects::ConstPointer parentNodes = dataStorage->GetSources(segmentationNode, NodePredicates::GetImagePredicate(), false); for (auto it = parentNodes->Begin(); it != parentNodes->End(); ++it) { DataNode::Pointer dataNode = it->Value(); allImagesOfLesion.push_back(it->Value()); } } std::sort(allImagesOfLesion.begin(), allImagesOfLesion.end()); allImagesOfLesion.erase(std::unique(allImagesOfLesion.begin(), allImagesOfLesion.end()), allImagesOfLesion.end()); return allImagesOfLesion; } mitk::SemanticRelationsDataStorageAccess::DataNodeVector mitk::SemanticRelationsDataStorageAccess::GetAllSpecificImages(const SemanticTypes::CaseID& caseID, const SemanticTypes::ControlPoint& controlPoint, const SemanticTypes::InformationType& informationType) const { if (SemanticRelationsInference::InstanceExists(caseID, controlPoint)) { if (SemanticRelationsInference::InstanceExists(caseID, informationType)) { // control point exists, information type exists, retrieve all images from the storage DataNodeVector allImagesOfCase = GetAllImagesOfCase(caseID); // filter all images to remove the ones with a different control point and information type using a lambda function auto lambda = [&controlPoint, &informationType](DataNode::Pointer imageNode) { return (informationType != SemanticRelationsInference::GetInformationTypeOfImage(imageNode)) || (controlPoint.date != SemanticRelationsInference::GetControlPointOfImage(imageNode).date); }; allImagesOfCase.erase(std::remove_if(allImagesOfCase.begin(), allImagesOfCase.end(), lambda), allImagesOfCase.end()); return allImagesOfCase; } else { mitkThrowException(SemanticRelationException) << "Could not find an existing information type for the given caseID " << caseID << " and information type " << informationType << "."; } } else { mitkThrowException(SemanticRelationException) << "Could not find an existing control point for the given caseID " << caseID << " and control point " << controlPoint.UID << "."; } } mitk::SemanticRelationsDataStorageAccess::DataNodeVector mitk::SemanticRelationsDataStorageAccess::GetAllSpecificImages(const SemanticTypes::CaseID& caseID, const SemanticTypes::InformationType& informationType, const SemanticTypes::ExaminationPeriod& examinationPeriod) const { if (SemanticRelationsInference::InstanceExists(caseID, informationType)) { if (SemanticRelationsInference::InstanceExists(caseID, examinationPeriod)) { // examination period exists, information type exists, retrieve all imageIDs from the storage auto allImageIDsOfExaminationPeriod = SemanticRelationsInference::GetAllImageIDsOfExaminationPeriod(caseID, examinationPeriod); // filter all images to remove the ones with a different information type using a lambda function auto lambda = [&caseID, &informationType](SemanticTypes::ID imageID) { return (informationType != RelationStorage::GetInformationTypeOfImage(caseID, imageID)); }; allImageIDsOfExaminationPeriod.erase(std::remove_if(allImageIDsOfExaminationPeriod.begin(), allImageIDsOfExaminationPeriod.end(), lambda), allImageIDsOfExaminationPeriod.end()); auto allImagesOfExaminationPeriod = GetAllImagesByID(allImageIDsOfExaminationPeriod); return allImagesOfExaminationPeriod; } else { mitkThrowException(SemanticRelationException) << "Could not find an existing examination period for the given caseID " << caseID << " and examination period " << examinationPeriod.name << "."; } } else { mitkThrowException(SemanticRelationException) << "Could not find an existing information type for the given caseID " << caseID << " and information type " << informationType << "."; } } mitk::SemanticRelationsDataStorageAccess::DataNodeVector mitk::SemanticRelationsDataStorageAccess::GetAllSpecificSegmentations(const SemanticTypes::CaseID& caseID, const SemanticTypes::ControlPoint& controlPoint, const SemanticTypes::InformationType& informationType) const { - if (m_DataStorage.IsExpired()) + auto dataStorage = m_DataStorage.Lock(); + + if (dataStorage.IsNull()) { mitkThrow() << "Not a valid data storage."; } DataNodeVector allSpecificImages; try { allSpecificImages = GetAllSpecificImages(caseID, controlPoint, informationType); } catch (SemanticRelationException& e) { mitkReThrow(e) << "Cannot get the specific segmentation."; } DataNodeVector allSpecificSegmentations; for (const auto& imageNode : allSpecificImages) { - DataStorage::SetOfObjects::ConstPointer segmentationNodes = m_DataStorage.Lock()->GetDerivations(imageNode, NodePredicates::GetSegmentationPredicate(), false); + DataStorage::SetOfObjects::ConstPointer segmentationNodes = dataStorage->GetDerivations(imageNode, NodePredicates::GetSegmentationPredicate(), false); for (auto it = segmentationNodes->Begin(); it != segmentationNodes->End(); ++it) { allSpecificSegmentations.push_back(it->Value()); } } return allSpecificSegmentations; } mitk::DataNode::Pointer mitk::SemanticRelationsDataStorageAccess::GetSpecificSegmentation(const SemanticTypes::CaseID& caseID, const SemanticTypes::ControlPoint& controlPoint, const SemanticTypes::InformationType& informationType, const SemanticTypes::Lesion& lesion) const { if (m_DataStorage.IsExpired()) { mitkThrow() << "Not a valid data storage."; } DataNodeVector allSpecificSegmentations; try { allSpecificSegmentations = GetAllSpecificSegmentations(caseID, controlPoint, informationType); } catch (SemanticRelationException& e) { mitkReThrow(e) << "Cannot get the specific segmentation."; } for (const auto& segmentationNode : allSpecificSegmentations) { SemanticTypes::Lesion representedLesion = SemanticRelationsInference::GetLesionOfSegmentation(segmentationNode); if (representedLesion.UID == lesion.UID) { return segmentationNode; } } return mitk::DataNode::Pointer(); } diff --git a/Modules/SemanticRelationsUI/src/QmitkLesionTreeModel.cpp b/Modules/SemanticRelationsUI/src/QmitkLesionTreeModel.cpp index c4f2923b2e..fddbc1637d 100644 --- a/Modules/SemanticRelationsUI/src/QmitkLesionTreeModel.cpp +++ b/Modules/SemanticRelationsUI/src/QmitkLesionTreeModel.cpp @@ -1,298 +1,298 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ // semantic relations UI module #include "QmitkLesionTreeModel.h" // semantic relations module #include #include #include #include #include #include // qt #include QmitkLesionTreeModel::QmitkLesionTreeModel(QObject* parent/* = nullptr*/) : QmitkAbstractSemanticRelationsStorageModel(parent) , m_LastSegmentation(nullptr) , m_RootItem(std::make_shared(mitk::LesionData())) { // 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(); } auto sharedParent = parentItem.lock(); if (sharedParent == m_RootItem) { return QModelIndex(); } return createIndex(sharedParent->GetRow(), 0, sharedParent.get()); } int QmitkLesionTreeModel::rowCount(const QModelIndex& itemIndex/* = QModelIndex()*/) const { return GetItemByIndex(itemIndex)->ChildCount(); } int QmitkLesionTreeModel::columnCount(const QModelIndex&/* itemIndex = QModelIndex() */) const { if (0 == m_RootItem->ChildCount()) { // no lesion items stored, no need to display columns return 0; } 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) { if (currentItem->GetParent().expired()) { return QVariant(); } auto parentItem = currentItem->GetParent().lock(); // parent exists and is the root item -> 1. item of a lesion entry if (m_RootItem == parentItem) { // display role fills the first columns with the lesion UID / name if (0 == index.column()) { std::string itemString = currentItem->GetData().GetLesionName(); if (itemString.empty()) { itemString = currentItem->GetData().GetLesionUID(); } return QString::fromStdString(itemString); } else { // display role fills other columns with the lesion presence info const auto lesionPresence = currentItem->GetData().GetLesionPresence(); if (index.column() - 1 > static_cast(lesionPresence.size())) { return "N/A"; } if (lesionPresence.at(index.column() - 1)) { return QString::fromStdString("present"); } return QString::fromStdString("not present"); } } } if (Qt::BackgroundColorRole == role) { auto it = m_DataNodePresence.find(currentItem->GetData().GetLesion().UID); if (it != m_DataNodePresence.end()) { return it->second ? QVariant(QColor(Qt::darkGreen)) : QVariant(QColor(Qt::transparent)); } return QVariant(QColor(Qt::transparent)); } if (Qt::UserRole == role) { return QVariant::fromValue(currentItem); } return QVariant(); } QVariant QmitkLesionTreeModel::headerData(int section, Qt::Orientation orientation, int role) const { if (0 == m_RootItem->ChildCount()) { // no lesion items stored, no need to display the header return QVariant(); } if (Qt::Horizontal == orientation && Qt::DisplayRole == role) { if (0 == section) { return QVariant("Lesion"); } if (static_cast(m_ControlPoints.size()) >= section) { mitk::SemanticTypes::ControlPoint currentControlPoint = m_ControlPoints.at(section-1); return QVariant(QString::fromStdString(currentControlPoint.ToString())); } } return QVariant(); } const mitk::DataNode* QmitkLesionTreeModel::GetLastSegmentation() const { return m_LastSegmentation; } void QmitkLesionTreeModel::NodeAdded(const mitk::DataNode* dataNode) { if (mitk::NodePredicates::GetSegmentationPredicate()->CheckNode(dataNode)) { m_LastSegmentation = dataNode; } } void QmitkLesionTreeModel::SetData() { m_RootItem = std::make_shared(mitk::LesionData()); // get all control points of current case m_ControlPoints = mitk::RelationStorage::GetAllControlPointsOfCase(m_CaseID); // sort the vector of control points for the timeline std::sort(m_ControlPoints.begin(), m_ControlPoints.end()); SetLesionData(); SetSelectedDataNodesPresence(); } void QmitkLesionTreeModel::SetLesionData() { m_CurrentLesions = mitk::RelationStorage::GetAllLesionsOfCase(m_CaseID); for (auto& lesion : m_CurrentLesions) { AddLesion(lesion); } } void QmitkLesionTreeModel::AddLesion(const mitk::SemanticTypes::Lesion& lesion) { - if (m_DataStorage.IsExpired()) + auto dataStorage = m_DataStorage.Lock(); + + if (dataStorage.IsNull()) { return; } - auto dataStorage = m_DataStorage.Lock(); - // create new lesion tree item data and modify it according to the control point data mitk::LesionData lesionData(lesion); mitk::ComputeLesionPresence(lesionData, m_CaseID); // add the top-level lesion item to the root item std::shared_ptr newLesionTreeItem = std::make_shared(lesionData); m_RootItem->AddChild(newLesionTreeItem); } void QmitkLesionTreeModel::SetSelectedDataNodesPresence() { m_DataNodePresence.clear(); for (const auto& dataNode : qAsConst(m_SelectedDataNodes)) { if (!mitk::SemanticRelationsInference::InstanceExists(dataNode)) { continue; } for (const auto& lesion : m_CurrentLesions) { if (!mitk::SemanticRelationsInference::InstanceExists(m_CaseID, lesion)) { continue; } try { // set the lesion presence for the current node bool dataNodePresence = mitk::SemanticRelationsInference::IsLesionPresent(lesion, dataNode); SetDataNodePresenceOfLesion(&lesion, dataNodePresence); } catch (const mitk::SemanticRelationException&) { continue; } } } } void QmitkLesionTreeModel::SetDataNodePresenceOfLesion(const mitk::SemanticTypes::Lesion* lesion, bool dataNodePresence) { std::map::iterator iter = m_DataNodePresence.find(lesion->UID); if (iter != m_DataNodePresence.end()) { // key already existing, overwrite already stored bool value iter->second = dataNodePresence; } else { m_DataNodePresence.insert(std::make_pair(lesion->UID, dataNodePresence)); } } 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/QmitkPatientTableInspector.cpp b/Modules/SemanticRelationsUI/src/QmitkPatientTableInspector.cpp index 6edf5ae5df..96d48e69cb 100644 --- a/Modules/SemanticRelationsUI/src/QmitkPatientTableInspector.cpp +++ b/Modules/SemanticRelationsUI/src/QmitkPatientTableInspector.cpp @@ -1,179 +1,179 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ // semantic relations UI module #include "QmitkPatientTableInspector.h" #include "QmitkPatientTableHeaderView.h" // mitk qt widgets module #include "QmitkCustomVariants.h" #include "QmitkEnums.h" // qt #include #include QmitkPatientTableInspector::QmitkPatientTableInspector(QWidget* parent/* =nullptr*/) : QmitkAbstractSemanticRelationsStorageInspector(parent) { m_Controls.setupUi(this); QmitkPatientTableHeaderView* patientTableHeaderView = new QmitkPatientTableHeaderView(m_Controls.tableView); m_Controls.tableView->setHorizontalHeader(patientTableHeaderView); m_Controls.tableView->horizontalHeader()->setHighlightSections(false); m_Controls.tableView->horizontalHeader()->setSectionResizeMode(QHeaderView::Fixed); m_Controls.tableView->verticalHeader()->setHighlightSections(false); m_Controls.tableView->verticalHeader()->setSectionResizeMode(QHeaderView::Fixed); m_Controls.tableView->setSelectionMode(QAbstractItemView::SingleSelection); m_Controls.tableView->setSelectionBehavior(QAbstractItemView::SelectItems); m_Controls.tableView->setContextMenuPolicy(Qt::CustomContextMenu); m_StorageModel = new QmitkPatientTableModel(m_Controls.tableView); m_Controls.tableView->setModel(m_StorageModel); m_ItemDelegate = new QmitkTableItemThumbnailDelegate(m_Controls.tableView); //m_Controls.tableView->setItemDelegate(m_ItemDelegate); SetUpConnections(); } QAbstractItemView* QmitkPatientTableInspector::GetView() { return m_Controls.tableView; } const QAbstractItemView* QmitkPatientTableInspector::GetView() const { return m_Controls.tableView; } void QmitkPatientTableInspector::SetSelectionMode(SelectionMode mode) { m_Controls.tableView->setSelectionMode(mode); } QmitkPatientTableInspector::SelectionMode QmitkPatientTableInspector::GetSelectionMode() const { return m_Controls.tableView->selectionMode(); } void QmitkPatientTableInspector::SetCaseID(const mitk::SemanticTypes::CaseID& caseID) { m_StorageModel->SetCaseID(caseID); } void QmitkPatientTableInspector::SetLesion(const mitk::SemanticTypes::Lesion& lesion) { m_StorageModel->SetLesion(lesion); } QItemSelectionModel* QmitkPatientTableInspector::GetSelectionModel() { return m_Controls.tableView->selectionModel(); } void QmitkPatientTableInspector::Initialize() { - if (m_DataStorage.IsExpired()) + auto dataStorage = m_DataStorage.Lock(); + + if (dataStorage.IsNull()) { return; } - auto dataStorage = m_DataStorage.Lock(); - m_StorageModel->SetDataStorage(dataStorage); m_StorageModel->SetNodePredicate(m_NodePredicate); m_Connector->SetView(m_Controls.tableView); } void QmitkPatientTableInspector::OnModelUpdated() { m_Controls.tableView->resizeRowsToContents(); m_Controls.tableView->resizeColumnsToContents(); } void QmitkPatientTableInspector::OnNodeButtonClicked(const QString& nodeType) { m_StorageModel->SetNodeType(nodeType.toStdString()); } void QmitkPatientTableInspector::OnDataNodeSelectionChanged(const QList& dataNodeSelection) { if (m_StorageModel->GetLesion().UID.empty()) { return; } // if lesion is set, reset to empty lesion to hide the "lesion presence background highlighting" in the model m_StorageModel->SetLesion(mitk::SemanticTypes::Lesion()); // need to explicitly set the data node selection SetCurrentSelection(dataNodeSelection); } void QmitkPatientTableInspector::OnItemDoubleClicked(const QModelIndex& itemIndex) { if (itemIndex.isValid()) { QVariant qvariantDataNode = m_StorageModel->data(itemIndex, QmitkDataNodeRawPointerRole); if (qvariantDataNode.canConvert()) { mitk::DataNode* dataNode = qvariantDataNode.value(); emit DataNodeDoubleClicked(dataNode); } } } void QmitkPatientTableInspector::SetUpConnections() { connect(m_StorageModel, &QmitkPatientTableModel::ModelUpdated, this, &QmitkPatientTableInspector::OnModelUpdated); connect(m_Controls.tableView, &QTableView::customContextMenuRequested, this, &QmitkPatientTableInspector::OnContextMenuRequested); QSignalMapper* nodeButtonSignalMapper = new QSignalMapper(this); nodeButtonSignalMapper->setMapping(m_Controls.imageNodeButton, QString("Image")); nodeButtonSignalMapper->setMapping(m_Controls.segmentationNodeButton, QString("Segmentation")); connect(nodeButtonSignalMapper, static_cast(&QSignalMapper::mapped), this, &QmitkPatientTableInspector::OnNodeButtonClicked); connect(m_Controls.imageNodeButton, &QRadioButton::clicked, nodeButtonSignalMapper, static_cast(&QSignalMapper::map)); connect(m_Controls.segmentationNodeButton, &QRadioButton::clicked, nodeButtonSignalMapper, static_cast(&QSignalMapper::map)); m_Controls.imageNodeButton->setChecked(true); connect(this, &QmitkPatientTableInspector::CurrentSelectionChanged, this, &QmitkPatientTableInspector::OnDataNodeSelectionChanged); connect(m_Controls.tableView, &QTableView::doubleClicked, this, &QmitkPatientTableInspector::OnItemDoubleClicked); } void QmitkPatientTableInspector::keyPressEvent(QKeyEvent* e) { mitk::DataNode* dataNode = nullptr; QModelIndex selectedIndex = m_Controls.tableView->currentIndex(); if (selectedIndex.isValid()) { QVariant qvariantDataNode = m_StorageModel->data(selectedIndex, QmitkDataNodeRawPointerRole); if (qvariantDataNode.canConvert()) { dataNode = qvariantDataNode.value(); } } if (nullptr == dataNode) { return; } int key = e->key(); switch (key) { case Qt::Key_Delete: emit OnNodeRemoved(dataNode); break; default: break; } } diff --git a/Modules/SemanticRelationsUI/src/QmitkStatisticsCalculator.cpp b/Modules/SemanticRelationsUI/src/QmitkStatisticsCalculator.cpp index 0caa074875..01e9882c3b 100644 --- a/Modules/SemanticRelationsUI/src/QmitkStatisticsCalculator.cpp +++ b/Modules/SemanticRelationsUI/src/QmitkStatisticsCalculator.cpp @@ -1,233 +1,232 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ // semantic relations UI module #include "QmitkStatisticsCalculator.h" // semantic relations module #include #include #include #include #include // mitk image statistics module #include #include #include #include QmitkStatisticsCalculator::QmitkStatisticsCalculator() : m_CalculationJob(nullptr) , m_DataStorage(nullptr) , m_MaskVolume(0.0) { m_CalculationJob = new QmitkImageStatisticsCalculationJob(); connect(m_CalculationJob, &QmitkImageStatisticsCalculationJob::finished, this, &QmitkStatisticsCalculator::OnStatisticsCalculationEnds, Qt::QueuedConnection); } QmitkStatisticsCalculator::~QmitkStatisticsCalculator() { if (!m_CalculationJob->isFinished()) { m_CalculationJob->terminate(); m_CalculationJob->wait(); } m_CalculationJob->deleteLater(); } void QmitkStatisticsCalculator::ComputeLesionVolume(mitk::LesionData& lesionData, const mitk::SemanticTypes::CaseID& caseID) { - if (m_DataStorage.IsExpired()) + auto dataStorage = m_DataStorage.Lock(); + + if (dataStorage.IsNull()) { return; } - auto dataStorage = m_DataStorage.Lock(); - std::vector lesionVolume; mitk::SemanticTypes::Lesion lesion = lesionData.GetLesion(); double volume = 0.0; mitk::SemanticTypes::ControlPointVector controlPoints = mitk::RelationStorage::GetAllControlPointsOfCase(caseID); // sort the vector of control points for the timeline std::sort(controlPoints.begin(), controlPoints.end()); mitk::SemanticTypes::InformationTypeVector informationTypes = mitk::RelationStorage::GetAllInformationTypesOfCase(caseID); for (const auto& informationType : informationTypes) { for (const auto& controlPoint : controlPoints) { mitk::SemanticRelationsDataStorageAccess semanticRelationsDataStorageAccess(dataStorage); mitk::DataNode::Pointer specificImage; mitk::DataNode::Pointer specificSegmentation; try { specificSegmentation = semanticRelationsDataStorageAccess.GetSpecificSegmentation(caseID, controlPoint, informationType, lesion); if (nullptr == specificSegmentation) { volume = 0.0; } else { // get parent node of the specific segmentation node with the node predicate auto parentNodes = dataStorage->GetSources(specificSegmentation, mitk::NodePredicates::GetImagePredicate(), false); for (auto it = parentNodes->Begin(); it != parentNodes->End(); ++it) { specificImage = it->Value(); } volume = GetSegmentationMaskVolume(specificImage, specificSegmentation); } } catch (mitk::SemanticRelationException&) { volume = 0.0; } lesionVolume.push_back(volume); } } lesionData.SetLesionVolume(lesionVolume); } double QmitkStatisticsCalculator::GetSegmentationMaskVolume(mitk::DataNode::Pointer imageNode, mitk::DataNode::Pointer segmentationNode) { m_MaskVolume = 0.0; + auto dataStorage = m_DataStorage.Lock(); - if (m_DataStorage.IsExpired()) + if (dataStorage.IsNull()) { return m_MaskVolume; } - auto dataStorage = m_DataStorage.Lock(); - if (imageNode.IsNull() || segmentationNode.IsNull()) { return m_MaskVolume; } m_ImageNode = imageNode; m_SegmentationNode = segmentationNode; auto image = dynamic_cast(m_ImageNode->GetData()); auto segmentation = dynamic_cast(m_SegmentationNode->GetData()); if (nullptr == image || nullptr == segmentation) { return m_MaskVolume; } // all nodes and images are valid, retrieve statistics mitk::ImageStatisticsContainer::ConstPointer imageStatistics = mitk::ImageStatisticsContainerManager::GetImageStatistics(dataStorage, image, segmentation).GetPointer(); bool imageStatisticsOlderThanInputs = false; if (imageStatistics && (imageStatistics->GetMTime() < image->GetMTime() || (imageStatistics->GetMTime() < segmentation->GetMTime()))) { imageStatisticsOlderThanInputs = true; } // statistics need to be (re)computed if (!imageStatistics || imageStatisticsOlderThanInputs) { m_CalculationJob->Initialize(image, segmentation, nullptr); try { m_CalculationJob->start(); return m_MaskVolume; } catch (const std::exception&) { return m_MaskVolume; } } // use a valid statistics object to get the volume of the image-segmentation pair mitk::ImageStatisticsContainer::ImageStatisticsObject statisticsObject; try { statisticsObject = imageStatistics->GetStatisticsForTimeStep(0); } catch (mitk::Exception&) { return m_MaskVolume; } try { if (statisticsObject.HasStatistic(mitk::ImageStatisticsConstants::VOLUME())) { auto valueVariant = statisticsObject.GetValueNonConverted(mitk::ImageStatisticsConstants::VOLUME()); m_MaskVolume = boost::get(valueVariant); } } catch (mitk::Exception&) { return m_MaskVolume; } return m_MaskVolume; } void QmitkStatisticsCalculator::OnStatisticsCalculationEnds() { // taken from 'QmitkImageStatisticsView' (see measurementtoolbox plugin) - if (m_DataStorage.IsExpired()) + auto dataStorage = m_DataStorage.Lock(); + + if (dataStorage.IsNull()) { return; } - auto dataStorage = m_DataStorage.Lock(); - if (m_CalculationJob->GetStatisticsUpdateSuccessFlag()) { auto statistic = m_CalculationJob->GetStatisticsData(); auto image = m_CalculationJob->GetStatisticsImage(); mitk::BaseData::ConstPointer mask = nullptr; auto imageRule = mitk::StatisticsToImageRelationRule::New(); imageRule->Connect(statistic, image); if (m_CalculationJob->GetMaskImage()) { auto maskRule = mitk::StatisticsToMaskRelationRule::New(); mask = m_CalculationJob->GetMaskImage(); maskRule->Connect(statistic, mask); } auto imageStatistics = mitk::ImageStatisticsContainerManager::GetImageStatistics(dataStorage, image, mask); // if statistics base data already exist: add to existing node if (nullptr != imageStatistics) { auto allDataNodes = dataStorage->GetAll()->CastToSTLConstContainer(); for (auto node : allDataNodes) { auto nodeData = node->GetData(); if (nullptr != nodeData && nodeData->GetUID() == imageStatistics->GetUID()) { node->SetData(statistic); } } } // statistics base data does not exist: add new node else { auto statisticsNodeName = m_ImageNode->GetName(); if (m_SegmentationNode) { statisticsNodeName += "_" + m_SegmentationNode->GetName(); } statisticsNodeName += "_statistics"; auto statisticsNode = mitk::CreateImageStatisticsNode(statistic, statisticsNodeName); dataStorage->Add(statisticsNode); } } } diff --git a/Modules/SemanticRelationsUI/src/QmitkStatisticsTreeModel.cpp b/Modules/SemanticRelationsUI/src/QmitkStatisticsTreeModel.cpp index fba6ca57f6..110bff79dc 100644 --- a/Modules/SemanticRelationsUI/src/QmitkStatisticsTreeModel.cpp +++ b/Modules/SemanticRelationsUI/src/QmitkStatisticsTreeModel.cpp @@ -1,267 +1,268 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ // semantic relations UI module #include "QmitkStatisticsTreeModel.h" // semantic relations module #include #include #include #include #include QmitkStatisticsTreeModel::QmitkStatisticsTreeModel(QObject* parent/* = nullptr*/) : QmitkAbstractSemanticRelationsStorageModel(parent) , m_RootItem(std::make_shared(mitk::LesionData())) { m_StatisticsCalculator = std::make_unique(); } ////////////////////////////////////////////////////////////////////////// // overridden virtual functions from QAbstractItemModel ////////////////////////////////////////////////////////////////////////// QModelIndex QmitkStatisticsTreeModel::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 QmitkStatisticsTreeModel::parent(const QModelIndex& itemIndex) const { if (!itemIndex.isValid()) { return QModelIndex(); } auto parentItemWeakPtr = GetItemByIndex(itemIndex)->GetParent(); if (parentItemWeakPtr.expired()) { return QModelIndex(); } auto parentItem = parentItemWeakPtr.lock(); if (parentItem == m_RootItem) { return QModelIndex(); } return createIndex(parentItem->GetRow(), 0, parentItem.get()); } int QmitkStatisticsTreeModel::rowCount(const QModelIndex& itemIndex/* = QModelIndex()*/) const { return GetItemByIndex(itemIndex)->ChildCount(); } int QmitkStatisticsTreeModel::columnCount(const QModelIndex&/* itemIndex = QModelIndex() */) const { if (0 == m_RootItem->ChildCount()) { // no lesion items stored, no need to display columns return 0; } return m_ControlPoints.size() + 1; } QVariant QmitkStatisticsTreeModel::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) { if (currentItem->GetParent().expired()) { return QVariant(); } auto parentItem = currentItem->GetParent().lock(); // parent exists and is the root item -> top level item if (m_RootItem == parentItem) { // display role fills the first columns with the lesion UID / name if (0 == index.column()) { std::string itemString = currentItem->GetData().GetLesionName(); if (itemString.empty()) { itemString = currentItem->GetData().GetLesionUID(); } return QString::fromStdString(itemString); } } // parent is not the root item -> volume item else { // display role fills the first columns with the information type if (0 == index.column()) { if (index.row() < static_cast(m_InformationTypes.size())) { return QString::fromStdString(m_InformationTypes.at(index.row())); } return "N/A"; } else { // display role fills other columns with the lesion volume info const auto lesionVolume = currentItem->GetData().GetLesionVolume(); if ((index.column() - 1) * index.row() < static_cast(lesionVolume.size())) { return QVariant(lesionVolume.at(index.row()*m_ControlPoints.size() + (index.column() - 1))); } return "N/A"; } } } return QVariant(); } QVariant QmitkStatisticsTreeModel::headerData(int section, Qt::Orientation orientation, int role) const { if (0 == m_RootItem->ChildCount()) { // no lesion items stored, no need to display the header return QVariant(); } if (Qt::Horizontal == orientation && Qt::DisplayRole == role) { if (0 == section) { return QVariant("Lesion"); } if (static_cast(m_ControlPoints.size()) >= section) { mitk::SemanticTypes::ControlPoint currentControlPoint = m_ControlPoints.at(section-1); return QVariant(QString::fromStdString(currentControlPoint.ToString())); } } return QVariant(); } void QmitkStatisticsTreeModel::DataStorageChanged() { - if (!m_DataStorage.IsExpired()) + auto dataStorage = m_DataStorage.Lock(); + + if (dataStorage.IsNotNull()) { - auto dataStorage = m_DataStorage.Lock(); m_SemanticRelationsDataStorageAccess = std::make_unique(dataStorage); m_StatisticsCalculator->SetDataStorage(dataStorage); UpdateModelData(); } } void QmitkStatisticsTreeModel::NodeAdded(const mitk::DataNode*) { emit beginResetModel(); UpdateModelData(); emit endResetModel(); } void QmitkStatisticsTreeModel::NodeChanged(const mitk::DataNode*) { emit beginResetModel(); UpdateModelData(); emit endResetModel(); } void QmitkStatisticsTreeModel::NodeRemoved(const mitk::DataNode*) { emit beginResetModel(); UpdateModelData(); emit endResetModel(); } void QmitkStatisticsTreeModel::SetData() { m_RootItem = std::make_shared(mitk::LesionData()); // get all control points of current case m_ControlPoints = mitk::RelationStorage::GetAllControlPointsOfCase(m_CaseID); // sort the vector of control points for the timeline std::sort(m_ControlPoints.begin(), m_ControlPoints.end()); // get all information types points of current case m_InformationTypes = mitk::RelationStorage::GetAllInformationTypesOfCase(m_CaseID); SetLesionData(); } void QmitkStatisticsTreeModel::SetLesionData() { m_CurrentLesions = mitk::RelationStorage::GetAllLesionsOfCase(m_CaseID); for (auto& lesion : m_CurrentLesions) { AddLesion(lesion); } } void QmitkStatisticsTreeModel::AddLesion(const mitk::SemanticTypes::Lesion& lesion) { - if (m_DataStorage.IsExpired()) + auto dataStorage = m_DataStorage.Lock(); + + if (dataStorage.IsNull()) { return; } - auto dataStorage = m_DataStorage.Lock(); - // create new lesion tree item data and modify it according to the control point data mitk::LesionData lesionData(lesion); m_StatisticsCalculator->ComputeLesionVolume(lesionData, m_CaseID); // add the 1. level lesion item to the root item std::shared_ptr newLesionTreeItem = std::make_shared(lesionData); m_RootItem->AddChild(newLesionTreeItem); for (size_t i = 0; i < m_InformationTypes.size(); ++i) { std::shared_ptr volumeItem = std::make_shared(lesionData); newLesionTreeItem->AddChild(volumeItem); } } QmitkLesionTreeItem* QmitkStatisticsTreeModel::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/Plugins/org.mitk.gui.qt.application/src/QmitkAbstractDataNodeAction.cpp b/Plugins/org.mitk.gui.qt.application/src/QmitkAbstractDataNodeAction.cpp index decbec0004..c706326752 100644 --- a/Plugins/org.mitk.gui.qt.application/src/QmitkAbstractDataNodeAction.cpp +++ b/Plugins/org.mitk.gui.qt.application/src/QmitkAbstractDataNodeAction.cpp @@ -1,120 +1,117 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #include #include "mitkIRenderWindowPart.h" // mitk gui common plugin #include // berry #include QList AbstractDataNodeAction::GetSelectedNodes(berry::IWorkbenchPartSite::Pointer workbenchPartSite) { QList selectedNodes; if (workbenchPartSite.IsNull()) { return selectedNodes; } berry::ISelection::ConstPointer selection = workbenchPartSite->GetWorkbenchWindow()->GetSelectionService()->GetSelection(); mitk::DataNodeSelection::ConstPointer currentSelection = selection.Cast(); if (currentSelection.IsNull() || currentSelection->IsEmpty()) { return selectedNodes; } selectedNodes = QList::fromStdList(currentSelection->GetSelectedDataNodes()); return selectedNodes; } QmitkAbstractDataNodeAction::QmitkAbstractDataNodeAction(berry::IWorkbenchPartSite::Pointer workbenchPartSite) { m_WorkbenchPartSite = workbenchPartSite; } QmitkAbstractDataNodeAction::QmitkAbstractDataNodeAction(berry::IWorkbenchPartSite* workbenchPartSite) { m_WorkbenchPartSite = berry::IWorkbenchPartSite::Pointer(workbenchPartSite); } void QmitkAbstractDataNodeAction::SetDataStorage(mitk::DataStorage* dataStorage) { if (m_DataStorage != dataStorage) { // set the new data storage m_DataStorage = dataStorage; } } void QmitkAbstractDataNodeAction::SetSelectedNodes(const QList& selectedNodes) { m_SelectedNodes = selectedNodes; // use the first selected node to initialize the data node actions InitializeWithDataNode(m_SelectedNodes.front()); } void QmitkAbstractDataNodeAction::SetBaseRenderer(mitk::BaseRenderer* baseRenderer) { if (m_BaseRenderer != baseRenderer) { // set the new base renderer m_BaseRenderer = baseRenderer; } } mitk::BaseRenderer::Pointer QmitkAbstractDataNodeAction::GetBaseRenderer() { - mitk::BaseRenderer::Pointer baseRenderer; - if (!m_BaseRenderer.IsExpired()) - { - baseRenderer = m_BaseRenderer.Lock(); - } - return baseRenderer; + return m_BaseRenderer.Lock(); } QList QmitkAbstractDataNodeAction::GetSelectedNodes() const { if (!m_SelectedNodes.isEmpty()) { return m_SelectedNodes; } - if (m_WorkbenchPartSite.Expired()) + auto workbenchPartSite = m_WorkbenchPartSite.Lock(); + + if (workbenchPartSite.IsNull()) { // return empty list of selected nodes return m_SelectedNodes; } // retrieve selection from the workbench selection service - return AbstractDataNodeAction::GetSelectedNodes(m_WorkbenchPartSite.Lock()); + return AbstractDataNodeAction::GetSelectedNodes(workbenchPartSite); } mitk::DataNode::Pointer QmitkAbstractDataNodeAction::GetSelectedNode() const { QList selectedNodes = GetSelectedNodes(); if (selectedNodes.empty()) { return nullptr; } // no batch action; should only be called with a single node mitk::DataNode::Pointer dataNode = selectedNodes.front(); if (nullptr == dataNode) { return nullptr; } return dataNode; } diff --git a/Plugins/org.mitk.gui.qt.application/src/QmitkDataNodeContextMenu.cpp b/Plugins/org.mitk.gui.qt.application/src/QmitkDataNodeContextMenu.cpp index a8a7e00ce6..9c3e7eea21 100644 --- a/Plugins/org.mitk.gui.qt.application/src/QmitkDataNodeContextMenu.cpp +++ b/Plugins/org.mitk.gui.qt.application/src/QmitkDataNodeContextMenu.cpp @@ -1,480 +1,483 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #include #include #include #include #include #include #include #include #include #include #include QmitkDataNodeContextMenu::QmitkDataNodeContextMenu(berry::IWorkbenchPartSite::Pointer workbenchPartSite, QWidget* parent) : QMenu(parent), m_Parent(parent), m_WorkbenchPartSite(workbenchPartSite) { this->InitNodeDescriptors(); this->InitDefaultActions(); this->InitExtensionPointActions(); } QmitkDataNodeContextMenu::~QmitkDataNodeContextMenu() { for (auto& descriptorActionPair : m_DescriptorActionList) descriptorActionPair.first->RemoveAction(descriptorActionPair.second); } void QmitkDataNodeContextMenu::SetDataStorage(mitk::DataStorage* dataStorage) { m_DataStorage = dataStorage; for (auto& descriptorActionPair : m_DescriptorActionList) { auto dataNodeAction = dynamic_cast(descriptorActionPair.second); if (nullptr != dataNodeAction) dataNodeAction->SetDataStorage(dataStorage); } } void QmitkDataNodeContextMenu::SetBaseRenderer(mitk::BaseRenderer* baseRenderer) { m_BaseRenderer = baseRenderer; for (auto& descriptorActionPair : m_DescriptorActionList) { auto dataNodeAction = dynamic_cast(descriptorActionPair.second); if (nullptr != dataNodeAction) dataNodeAction->SetBaseRenderer(baseRenderer); } } void QmitkDataNodeContextMenu::SetSurfaceDecimation(bool surfaceDecimation) { m_SurfaceDecimation = surfaceDecimation; } void QmitkDataNodeContextMenu::SetSelectedNodes(const QList& selectedNodes) { m_SelectedNodes = selectedNodes; } void QmitkDataNodeContextMenu::InitNodeDescriptors() { auto nodeDescriptorManager = QmitkNodeDescriptorManager::GetInstance(); m_UnknownDataNodeDescriptor = nodeDescriptorManager->GetUnknownDataNodeDescriptor(); m_ImageDataNodeDescriptor = nodeDescriptorManager->GetDescriptor("Image"); m_MultiComponentImageDataNodeDescriptor = nodeDescriptorManager->GetDescriptor("MultiComponentImage"); m_DiffusionImageDataNodeDescriptor = nodeDescriptorManager->GetDescriptor("DiffusionImage"); m_FiberBundleDataNodeDescriptor = nodeDescriptorManager->GetDescriptor("FiberBundle"); m_PeakImageDataNodeDescriptor = nodeDescriptorManager->GetDescriptor("PeakImage"); m_SegmentDataNodeDescriptor = nodeDescriptorManager->GetDescriptor("Segment"); m_SurfaceDataNodeDescriptor = nodeDescriptorManager->GetDescriptor("Surface"); m_PointSetNodeDescriptor = nodeDescriptorManager->GetDescriptor("PointSet"); m_PlanarLineNodeDescriptor = nodeDescriptorManager->GetDescriptor("PlanarLine"); m_PlanarCircleNodeDescriptor = nodeDescriptorManager->GetDescriptor("PlanarCircle"); m_PlanarEllipseNodeDescriptor = nodeDescriptorManager->GetDescriptor("PlanarEllipse"); m_PlanarAngleNodeDescriptor = nodeDescriptorManager->GetDescriptor("PlanarAngle"); m_PlanarFourPointAngleNodeDescriptor = nodeDescriptorManager->GetDescriptor("PlanarFourPointAngle"); m_PlanarRectangleNodeDescriptor = nodeDescriptorManager->GetDescriptor("PlanarRectangle"); m_PlanarPolygonNodeDescriptor = nodeDescriptorManager->GetDescriptor("PlanarPolygon"); m_PlanarPathNodeDescriptor = nodeDescriptorManager->GetDescriptor("PlanarPath"); m_PlanarDoubleEllipseNodeDescriptor = nodeDescriptorManager->GetDescriptor("PlanarDoubleEllipse"); m_PlanarBezierCurveNodeDescriptor = nodeDescriptorManager->GetDescriptor("PlanarBezierCurve"); m_PlanarSubdivisionPolygonNodeDescriptor = nodeDescriptorManager->GetDescriptor("PlanarSubdivisionPolygon"); } void QmitkDataNodeContextMenu::InitDefaultActions() { auto workbenchPartSite = m_WorkbenchPartSite.Lock(); m_GlobalReinitAction = new QmitkDataNodeGlobalReinitAction(m_Parent, workbenchPartSite); m_GlobalReinitAction->setIcon(QIcon(":/org.mitk.gui.qt.datamanager/Refresh_48.png")); m_UnknownDataNodeDescriptor->AddAction(m_GlobalReinitAction, true); m_DescriptorActionList.push_back(std::make_pair(m_UnknownDataNodeDescriptor, m_GlobalReinitAction)); m_ReinitAction = new QmitkDataNodeReinitAction(m_Parent, workbenchPartSite); m_ReinitAction->setIcon(QIcon(":/org.mitk.gui.qt.datamanager/Refresh_48.png")); m_UnknownDataNodeDescriptor->AddAction(m_ReinitAction, true); m_DescriptorActionList.push_back(std::make_pair(m_UnknownDataNodeDescriptor, m_ReinitAction)); QAction* saveAction = new QmitkFileSaveAction(QIcon(":/org.mitk.gui.qt.datamanager/Save_48.png"), workbenchPartSite->GetWorkbenchWindow()); m_UnknownDataNodeDescriptor->AddAction(saveAction, true); m_DescriptorActionList.push_back(std::make_pair(m_UnknownDataNodeDescriptor, saveAction)); m_RemoveAction = new QmitkDataNodeRemoveAction(m_Parent, workbenchPartSite); m_RemoveAction->setIcon(QIcon(":/org.mitk.gui.qt.datamanager/Remove_48.png")); m_UnknownDataNodeDescriptor->AddAction(m_RemoveAction, true); m_DescriptorActionList.push_back(std::make_pair(m_UnknownDataNodeDescriptor, m_RemoveAction)); m_ShowSelectedNodesAction = new QmitkDataNodeShowSelectedNodesAction(m_Parent, workbenchPartSite); m_RemoveAction->setIcon(QIcon(":/org.mitk.gui.qt.datamanager/ShowSelectedNode_48.png")); m_UnknownDataNodeDescriptor->AddAction(m_ShowSelectedNodesAction, true); m_DescriptorActionList.push_back(std::make_pair(m_UnknownDataNodeDescriptor, m_ShowSelectedNodesAction)); m_ToggleVisibilityAction = new QmitkDataNodeToggleVisibilityAction(m_Parent, workbenchPartSite); m_ToggleVisibilityAction->setIcon(QIcon(":/org.mitk.gui.qt.datamanager/InvertShowSelectedNode_48.png")); m_UnknownDataNodeDescriptor->AddAction(m_ToggleVisibilityAction, true); m_DescriptorActionList.push_back(std::make_pair(m_UnknownDataNodeDescriptor, m_ToggleVisibilityAction)); m_ShowDetailsAction = new QmitkDataNodeShowDetailsAction(m_Parent, workbenchPartSite); m_ShowDetailsAction->setIcon(QIcon(":/org.mitk.gui.qt.datamanager/ShowDataInfo_48.png")); m_UnknownDataNodeDescriptor->AddAction(m_ShowDetailsAction, true); m_DescriptorActionList.push_back(std::make_pair(m_UnknownDataNodeDescriptor, m_ShowDetailsAction)); m_OpacityAction = new QmitkDataNodeOpacityAction(m_Parent, workbenchPartSite); m_UnknownDataNodeDescriptor->AddAction(m_OpacityAction, false); m_DescriptorActionList.push_back(std::make_pair(m_UnknownDataNodeDescriptor, m_OpacityAction)); m_ColorAction = new QmitkDataNodeColorAction(m_Parent, workbenchPartSite); this->AddColorAction(m_ColorAction); m_ColormapAction = new QmitkDataNodeColorMapAction(m_Parent, workbenchPartSite); m_ImageDataNodeDescriptor->AddAction(m_ColormapAction); m_DescriptorActionList.push_back(std::make_pair(m_ImageDataNodeDescriptor, m_ColormapAction)); if (nullptr != m_DiffusionImageDataNodeDescriptor) { m_DiffusionImageDataNodeDescriptor->AddAction(m_ColormapAction, false); m_DescriptorActionList.push_back(std::make_pair(m_DiffusionImageDataNodeDescriptor, m_ColormapAction)); } m_ComponentAction = new QmitkDataNodeComponentAction(m_Parent, workbenchPartSite); m_MultiComponentImageDataNodeDescriptor->AddAction(m_ComponentAction, false); m_DescriptorActionList.push_back(std::make_pair(m_MultiComponentImageDataNodeDescriptor, m_ComponentAction)); if (nullptr != m_DiffusionImageDataNodeDescriptor) { m_DiffusionImageDataNodeDescriptor->AddAction(m_ComponentAction, false); m_DescriptorActionList.push_back(std::make_pair(m_DiffusionImageDataNodeDescriptor, m_ComponentAction)); } m_TextureInterpolationAction = new QmitkDataNodeTextureInterpolationAction(m_Parent, workbenchPartSite); m_ImageDataNodeDescriptor->AddAction(m_TextureInterpolationAction, false); m_DescriptorActionList.push_back(std::make_pair(m_ImageDataNodeDescriptor, m_TextureInterpolationAction)); if (nullptr != m_DiffusionImageDataNodeDescriptor) { m_DiffusionImageDataNodeDescriptor->AddAction(m_TextureInterpolationAction, false); m_DescriptorActionList.push_back(std::make_pair(m_DiffusionImageDataNodeDescriptor, m_TextureInterpolationAction)); } if (nullptr != m_SegmentDataNodeDescriptor) { m_SegmentDataNodeDescriptor->AddAction(m_TextureInterpolationAction, false); m_DescriptorActionList.push_back(std::make_pair(m_SegmentDataNodeDescriptor, m_TextureInterpolationAction)); } m_SurfaceRepresentationAction = new QmitkDataNodeSurfaceRepresentationAction(m_Parent, workbenchPartSite); m_SurfaceDataNodeDescriptor->AddAction(m_SurfaceRepresentationAction, false); m_DescriptorActionList.push_back(std::make_pair(m_SurfaceDataNodeDescriptor, m_SurfaceRepresentationAction)); } void QmitkDataNodeContextMenu::InitExtensionPointActions() { auto extensionPointService = berry::Platform::GetExtensionRegistry(); auto customMenuConfigs = extensionPointService->GetConfigurationElementsFor("org.mitk.gui.qt.datamanager.contextMenuActions"); DescriptorActionListType descriptorActionList; m_ConfigElements.clear(); for (const auto& customMenuConfig : qAsConst(customMenuConfigs)) { auto descriptorName = customMenuConfig->GetAttribute("nodeDescriptorName"); auto actionLabel = customMenuConfig->GetAttribute("label"); auto actionClass = customMenuConfig->GetAttribute("class"); if (descriptorName.isEmpty() || actionLabel.isEmpty() || actionClass.isEmpty()) continue; auto descriptor = QmitkNodeDescriptorManager::GetInstance()->GetDescriptor(descriptorName); if (nullptr == descriptor) { MITK_WARN << "Cannot add action \"" << actionLabel << "\" to non-existent descriptor \"" << descriptorName << "\"."; continue; } QAction* action = nullptr; auto actionIcon = customMenuConfig->GetAttribute("icon"); if (!actionIcon.isEmpty()) { QIcon icon = !QFile::exists(actionIcon) ? berry::AbstractUICTKPlugin::ImageDescriptorFromPlugin(customMenuConfig->GetContributor()->GetName(), actionIcon) : QIcon(actionIcon); action = new QAction(icon, actionLabel, m_Parent); } else { action = new QAction(actionLabel, m_Parent); } if (nullptr != action) { // See T26938. We do not know why but without the lambda function indirection, the // connection is lost after the content menu was shown for the first time. connect(action, &QAction::triggered, [action, this]() { this->OnExtensionPointActionTriggered(action); }); m_ConfigElements[action] = customMenuConfig; descriptorActionList.push_back(std::make_pair(descriptor, action)); } } this->AddDescriptorActionList(descriptorActionList); } void QmitkDataNodeContextMenu::InitServiceActions() { } void QmitkDataNodeContextMenu::OnContextMenuRequested(const QPoint& /*pos*/) { - if (m_WorkbenchPartSite.Expired()) + auto workbenchPartSite = m_WorkbenchPartSite.Lock(); + + if (workbenchPartSite.IsNull()) return; - auto selection = m_WorkbenchPartSite.Lock()->GetWorkbenchWindow()->GetSelectionService()->GetSelection() + auto selection = workbenchPartSite->GetWorkbenchWindow()->GetSelectionService()->GetSelection() .Cast(); if (selection.IsNull() || selection->IsEmpty()) return; m_SelectedNodes = QList::fromStdList(selection->GetSelectedDataNodes()); if (!m_SelectedNodes.isEmpty()) { this->clear(); auto actions = m_SelectedNodes.size() == 1 ? this->GetActions(m_SelectedNodes.front()) : this->GetActions(m_SelectedNodes); for (auto& action : actions) { auto dataNodeAction = dynamic_cast(action); if (nullptr != dataNodeAction) dataNodeAction->SetSelectedNodes(m_SelectedNodes); } this->addActions(actions); this->popup(QCursor::pos()); } } void QmitkDataNodeContextMenu::OnExtensionPointActionTriggered(QAction* action) { auto configElementIter = m_ConfigElements.find(action); if (m_ConfigElements.end() == configElementIter) { MITK_WARN << "Associated configuration element for action \"" << action->text() << "\" not found."; return; } auto configElement = configElementIter->second; auto contextMenuAction = configElement->CreateExecutableExtension("class"); + auto dataStorage = m_DataStorage.Lock(); - if (!m_DataStorage.IsExpired()) - contextMenuAction->SetDataStorage(m_DataStorage.Lock()); + if (dataStorage.IsNotNull()) + contextMenuAction->SetDataStorage(dataStorage); if ("QmitkCreatePolygonModelAction" == configElement->GetAttribute("class")) { contextMenuAction->SetSmoothed("true" == configElement->GetAttribute("smoothed")); contextMenuAction->SetDecimated(m_SurfaceDecimation); } contextMenuAction->Run(m_SelectedNodes); } void QmitkDataNodeContextMenu::AddColorAction(QWidgetAction* colorAction) { if (nullptr != m_ImageDataNodeDescriptor) { m_ImageDataNodeDescriptor->AddAction(colorAction, true); m_DescriptorActionList.push_back(std::make_pair(m_ImageDataNodeDescriptor, colorAction)); } if (nullptr != m_MultiComponentImageDataNodeDescriptor) { m_MultiComponentImageDataNodeDescriptor->AddAction(colorAction, true); m_DescriptorActionList.push_back(std::make_pair(m_MultiComponentImageDataNodeDescriptor, colorAction)); } if (nullptr != m_DiffusionImageDataNodeDescriptor) { m_DiffusionImageDataNodeDescriptor->AddAction(colorAction, true); m_DescriptorActionList.push_back(std::make_pair(m_DiffusionImageDataNodeDescriptor, colorAction)); } if (nullptr != m_FiberBundleDataNodeDescriptor) { m_FiberBundleDataNodeDescriptor->AddAction(colorAction, false); m_DescriptorActionList.push_back(std::make_pair(m_FiberBundleDataNodeDescriptor, colorAction)); } if (nullptr != m_PeakImageDataNodeDescriptor) { m_PeakImageDataNodeDescriptor->AddAction(colorAction, false); m_DescriptorActionList.push_back(std::make_pair(m_PeakImageDataNodeDescriptor, colorAction)); } if (nullptr != m_SegmentDataNodeDescriptor) { m_SegmentDataNodeDescriptor->AddAction(colorAction, false); m_DescriptorActionList.push_back(std::make_pair(m_SegmentDataNodeDescriptor, colorAction)); } if (nullptr != m_SurfaceDataNodeDescriptor) { m_SurfaceDataNodeDescriptor->AddAction(colorAction, true); m_DescriptorActionList.push_back(std::make_pair(m_SurfaceDataNodeDescriptor, colorAction)); } if (nullptr != m_PointSetNodeDescriptor) { m_PointSetNodeDescriptor->AddAction(colorAction, true); m_DescriptorActionList.push_back(std::make_pair(m_PointSetNodeDescriptor, colorAction)); } if (nullptr != m_PlanarLineNodeDescriptor) { m_PlanarLineNodeDescriptor->AddAction(colorAction, true); m_DescriptorActionList.push_back(std::make_pair(m_PlanarLineNodeDescriptor, colorAction)); } if (nullptr != m_PlanarCircleNodeDescriptor) { m_PlanarCircleNodeDescriptor->AddAction(colorAction, true); m_DescriptorActionList.push_back(std::make_pair(m_PlanarCircleNodeDescriptor, colorAction)); } if (nullptr != m_PlanarEllipseNodeDescriptor) { m_PlanarEllipseNodeDescriptor->AddAction(colorAction, true); m_DescriptorActionList.push_back(std::make_pair(m_PlanarEllipseNodeDescriptor, colorAction)); } if (nullptr != m_PlanarAngleNodeDescriptor) { m_PlanarAngleNodeDescriptor->AddAction(colorAction, true); m_DescriptorActionList.push_back(std::make_pair(m_PlanarAngleNodeDescriptor, colorAction)); } if (nullptr != m_PlanarFourPointAngleNodeDescriptor) { m_PlanarFourPointAngleNodeDescriptor->AddAction(colorAction, true); m_DescriptorActionList.push_back(std::make_pair(m_PlanarFourPointAngleNodeDescriptor, colorAction)); } if (nullptr != m_PlanarRectangleNodeDescriptor) { m_PlanarRectangleNodeDescriptor->AddAction(colorAction, true); m_DescriptorActionList.push_back(std::make_pair(m_PlanarRectangleNodeDescriptor, colorAction)); } if (nullptr != m_PlanarPolygonNodeDescriptor) { m_PlanarPolygonNodeDescriptor->AddAction(colorAction, true); m_DescriptorActionList.push_back(std::make_pair(m_PlanarPolygonNodeDescriptor, colorAction)); } if (nullptr != m_PlanarPathNodeDescriptor) { m_PlanarPathNodeDescriptor->AddAction(colorAction, true); m_DescriptorActionList.push_back(std::make_pair(m_PlanarPathNodeDescriptor, colorAction)); } if (nullptr != m_PlanarDoubleEllipseNodeDescriptor) { m_PlanarDoubleEllipseNodeDescriptor->AddAction(colorAction, true); m_DescriptorActionList.push_back(std::make_pair(m_PlanarDoubleEllipseNodeDescriptor, colorAction)); } if (nullptr != m_PlanarBezierCurveNodeDescriptor) { m_PlanarBezierCurveNodeDescriptor->AddAction(colorAction, true); m_DescriptorActionList.push_back(std::make_pair(m_PlanarBezierCurveNodeDescriptor, colorAction)); } if (nullptr != m_PlanarSubdivisionPolygonNodeDescriptor) { m_PlanarSubdivisionPolygonNodeDescriptor->AddAction(colorAction, true); m_DescriptorActionList.push_back(std::make_pair(m_PlanarSubdivisionPolygonNodeDescriptor, colorAction)); } } void QmitkDataNodeContextMenu::AddDescriptorActionList(DescriptorActionListType& descriptorActionList) { using ListItem = std::pair; std::sort(descriptorActionList.begin(), descriptorActionList.end(), [](const ListItem& left, const ListItem& right) -> bool { return left.second->text() < right.second->text(); }); for (auto& descriptorActionPair : descriptorActionList) { descriptorActionPair.first->AddAction(descriptorActionPair.second); m_DescriptorActionList.push_back(descriptorActionPair); } } QList QmitkDataNodeContextMenu::GetActions(const mitk::DataNode* node) { QList actions; for(const auto& descriptorActionPair : m_DescriptorActionList) { if (descriptorActionPair.first->CheckNode(node) || "Unknown" == descriptorActionPair.first->GetNameOfClass()) actions.append(descriptorActionPair.second); } return actions; } QList QmitkDataNodeContextMenu::GetActions(const QList& nodes) { QList actions; for (const auto& descriptorActionPair : m_DescriptorActionList) { for (const auto& node : nodes) { if (descriptorActionPair.first->CheckNode(node) || "Unknown" == descriptorActionPair.first->GetNameOfClass()) { auto batchActions = descriptorActionPair.first->GetBatchActions(); if (std::find(batchActions.begin(), batchActions.end(), descriptorActionPair.second) != batchActions.end()) actions.append(descriptorActionPair.second); break; } } } return actions; } diff --git a/Plugins/org.mitk.gui.qt.application/src/QmitkDataNodeGlobalReinitAction.cpp b/Plugins/org.mitk.gui.qt.application/src/QmitkDataNodeGlobalReinitAction.cpp index 1d86c3658a..7529cc5fbc 100644 --- a/Plugins/org.mitk.gui.qt.application/src/QmitkDataNodeGlobalReinitAction.cpp +++ b/Plugins/org.mitk.gui.qt.application/src/QmitkDataNodeGlobalReinitAction.cpp @@ -1,77 +1,81 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #include // mitk core #include // mitk gui common plugin #include const QString QmitkDataNodeGlobalReinitAction::ACTION_ID = "org.mitk.gui.qt.application.globalreinitaction"; // namespace that contains the concrete action namespace GlobalReinitAction { void Run(berry::IWorkbenchPartSite::Pointer workbenchPartSite, mitk::DataStorage::Pointer dataStorage) { auto renderWindow = mitk::WorkbenchUtil::GetRenderWindowPart(workbenchPartSite->GetPage(), mitk::WorkbenchUtil::NONE); if (nullptr == renderWindow) { renderWindow = mitk::WorkbenchUtil::OpenRenderWindowPart(workbenchPartSite->GetPage(), false); if (nullptr == renderWindow) { // no render window available return; } } mitk::RenderingManager::GetInstance()->InitializeViewsByBoundingObjects(dataStorage); } } QmitkDataNodeGlobalReinitAction::QmitkDataNodeGlobalReinitAction(QWidget* parent, berry::IWorkbenchPartSite::Pointer workbenchPartSite) : QAction(parent) , QmitkAbstractDataNodeAction(workbenchPartSite) { setText(tr("Global Reinit")); InitializeAction(); } QmitkDataNodeGlobalReinitAction::QmitkDataNodeGlobalReinitAction(QWidget* parent, berry::IWorkbenchPartSite* workbenchPartSite) : QAction(parent) , QmitkAbstractDataNodeAction(berry::IWorkbenchPartSite::Pointer(workbenchPartSite)) { setText(tr("Global Reinit")); InitializeAction(); } void QmitkDataNodeGlobalReinitAction::InitializeAction() { connect(this, &QmitkDataNodeGlobalReinitAction::triggered, this, &QmitkDataNodeGlobalReinitAction::OnActionTriggered); } void QmitkDataNodeGlobalReinitAction::OnActionTriggered(bool /*checked*/) { - if (m_WorkbenchPartSite.Expired()) + auto workbenchPartSite = m_WorkbenchPartSite.Lock(); + + if (workbenchPartSite.IsNull()) { return; } - if (m_DataStorage.IsExpired()) + auto dataStorage = m_DataStorage.Lock(); + + if (dataStorage.IsNull()) { return; } - GlobalReinitAction::Run(m_WorkbenchPartSite.Lock(), m_DataStorage.Lock()); + GlobalReinitAction::Run(workbenchPartSite, dataStorage); } diff --git a/Plugins/org.mitk.gui.qt.application/src/QmitkDataNodeReinitAction.cpp b/Plugins/org.mitk.gui.qt.application/src/QmitkDataNodeReinitAction.cpp index 1a62a732dc..622d38362f 100644 --- a/Plugins/org.mitk.gui.qt.application/src/QmitkDataNodeReinitAction.cpp +++ b/Plugins/org.mitk.gui.qt.application/src/QmitkDataNodeReinitAction.cpp @@ -1,133 +1,137 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #include // mitk core #include #include #include #include #include // mitk gui common plugin #include // namespace that contains the concrete action namespace ReinitAction { void Run(berry::IWorkbenchPartSite::Pointer workbenchPartSite, mitk::DataStorage::Pointer dataStorage, const QList& selectedNodes /*= QList()*/, mitk::BaseRenderer* baseRenderer /*= nullptr*/) { if (selectedNodes.empty()) { return; } if (workbenchPartSite.IsNotNull()) { auto renderWindow = mitk::WorkbenchUtil::GetRenderWindowPart(workbenchPartSite->GetPage(), mitk::WorkbenchUtil::NONE); if (nullptr == renderWindow) { renderWindow = mitk::WorkbenchUtil::OpenRenderWindowPart(workbenchPartSite->GetPage(), false); if (nullptr == renderWindow) { // no render window available return; } } } auto boundingBoxPredicate = mitk::NodePredicateNot::New(mitk::NodePredicateProperty::New("includeInBoundingBox", mitk::BoolProperty::New(false), baseRenderer)); mitk::DataStorage::SetOfObjects::Pointer nodes = mitk::DataStorage::SetOfObjects::New(); for (const auto& dataNode : selectedNodes) { if (boundingBoxPredicate->CheckNode(dataNode)) { nodes->InsertElement(nodes->Size(), dataNode); } } if (nodes->empty()) { return; } if (1 == nodes->Size()) // Special case: If exactly one ... { auto image = dynamic_cast(nodes->ElementAt(0)->GetData()); if (nullptr != image) // ... image is selected, reinit is expected to rectify askew images. { if (nullptr == baseRenderer) { mitk::RenderingManager::GetInstance()->InitializeViews(image->GetTimeGeometry()); } else { mitk::RenderingManager::GetInstance()->InitializeView(baseRenderer->GetRenderWindow(), image->GetTimeGeometry()); } return; } } auto boundingGeometry = dataStorage->ComputeBoundingGeometry3D(nodes, "visible", baseRenderer); if (nullptr == baseRenderer) { mitk::RenderingManager::GetInstance()->InitializeViews(boundingGeometry); } else { mitk::RenderingManager::GetInstance()->InitializeView(baseRenderer->GetRenderWindow(), boundingGeometry); } } } QmitkDataNodeReinitAction::QmitkDataNodeReinitAction(QWidget* parent, berry::IWorkbenchPartSite::Pointer workbenchpartSite) : QAction(parent) , QmitkAbstractDataNodeAction(workbenchpartSite) { setText(tr("Reinit")); InitializeAction(); } QmitkDataNodeReinitAction::QmitkDataNodeReinitAction(QWidget* parent, berry::IWorkbenchPartSite* workbenchpartSite) : QAction(parent) , QmitkAbstractDataNodeAction(berry::IWorkbenchPartSite::Pointer(workbenchpartSite)) { setText(tr("Reinit")); InitializeAction(); } void QmitkDataNodeReinitAction::InitializeAction() { connect(this, &QmitkDataNodeReinitAction::triggered, this, &QmitkDataNodeReinitAction::OnActionTriggered); } void QmitkDataNodeReinitAction::OnActionTriggered(bool /*checked*/) { - if (m_WorkbenchPartSite.Expired()) + auto workbenchPartSite = m_WorkbenchPartSite.Lock(); + + if (workbenchPartSite.IsNull()) { return; } - if (m_DataStorage.IsExpired()) + auto dataStorage = m_DataStorage.Lock(); + + if (dataStorage.IsNull()) { return; } mitk::BaseRenderer::Pointer baseRenderer = GetBaseRenderer(); auto selectedNodes = GetSelectedNodes(); - ReinitAction::Run(m_WorkbenchPartSite.Lock(), m_DataStorage.Lock(), selectedNodes, baseRenderer); + ReinitAction::Run(workbenchPartSite, dataStorage, selectedNodes, baseRenderer); } diff --git a/Plugins/org.mitk.gui.qt.application/src/QmitkDataNodeRemoveAction.cpp b/Plugins/org.mitk.gui.qt.application/src/QmitkDataNodeRemoveAction.cpp index bd7020d5d1..d27dc9c17a 100644 --- a/Plugins/org.mitk.gui.qt.application/src/QmitkDataNodeRemoveAction.cpp +++ b/Plugins/org.mitk.gui.qt.application/src/QmitkDataNodeRemoveAction.cpp @@ -1,114 +1,118 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #include #include // qt #include // berry #include #include #include namespace RemoveAction { void Run(berry::IWorkbenchPartSite::Pointer workbenchPartSite, mitk::DataStorage::Pointer dataStorage, const QList& selectedNodes, QWidget* parent /* = nullptr*/) { if (selectedNodes.empty()) { return; } QString question("Do you really want to remove "); for (auto& dataNode : selectedNodes) { if (nullptr == dataNode) { continue; } question.append(QString::fromStdString(dataNode->GetName())); question.append(", "); } // remove the last two characters = ", " question = question.remove(question.size() - 2, 2); question.append(" from data storage?"); QMessageBox::StandardButton answerButton = QMessageBox::question(parent, "DataManager", question, QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes); if (answerButton == QMessageBox::Yes) { for (auto& dataNode : selectedNodes) { if (nullptr == dataNode) { continue; } dataStorage->Remove(dataNode); } berry::IPreferencesService* prefService = berry::Platform::GetPreferencesService(); berry::IPreferences::Pointer preferencesNode = prefService->GetSystemPreferences()->Node(QmitkDataNodeGlobalReinitAction::ACTION_ID); bool globalReinit = preferencesNode->GetBool("Call global reinit if node is deleted", true); if (globalReinit) { GlobalReinitAction::Run(workbenchPartSite, dataStorage); } } } } QmitkDataNodeRemoveAction::QmitkDataNodeRemoveAction(QWidget* parent, berry::IWorkbenchPartSite::Pointer workbenchpartSite) : QAction(parent) , QmitkAbstractDataNodeAction(workbenchpartSite) { setText(tr("Remove")); m_Parent = parent; InitializeAction(); } QmitkDataNodeRemoveAction::QmitkDataNodeRemoveAction(QWidget* parent, berry::IWorkbenchPartSite* workbenchpartSite) : QAction(parent) , QmitkAbstractDataNodeAction(berry::IWorkbenchPartSite::Pointer(workbenchpartSite)) { setText(tr("Remove")); m_Parent = parent; InitializeAction(); } void QmitkDataNodeRemoveAction::InitializeAction() { connect(this, &QmitkDataNodeRemoveAction::triggered, this, &QmitkDataNodeRemoveAction::OnActionTriggered); } void QmitkDataNodeRemoveAction::OnActionTriggered(bool /*checked*/) { - if (m_WorkbenchPartSite.Expired()) + auto workbenchPartSite = m_WorkbenchPartSite.Lock(); + + if (workbenchPartSite.IsNull()) { return; } - if (m_DataStorage.IsExpired()) + auto dataStorage = m_DataStorage.Lock(); + + if (dataStorage.IsNull()) { return; } auto selectedNodes = GetSelectedNodes(); - RemoveAction::Run(m_WorkbenchPartSite.Lock(), m_DataStorage.Lock(), selectedNodes, m_Parent); + RemoveAction::Run(workbenchPartSite, dataStorage, selectedNodes, m_Parent); } diff --git a/Plugins/org.mitk.gui.qt.application/src/QmitkDataNodeShowSelectedNodesAction.cpp b/Plugins/org.mitk.gui.qt.application/src/QmitkDataNodeShowSelectedNodesAction.cpp index 368f6d289d..f9a998d19c 100644 --- a/Plugins/org.mitk.gui.qt.application/src/QmitkDataNodeShowSelectedNodesAction.cpp +++ b/Plugins/org.mitk.gui.qt.application/src/QmitkDataNodeShowSelectedNodesAction.cpp @@ -1,71 +1,71 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #include #include // mitk core #include QmitkDataNodeShowSelectedNodesAction::QmitkDataNodeShowSelectedNodesAction(QWidget* parent, berry::IWorkbenchPartSite::Pointer workbenchpartSite) : QAction(parent) , QmitkAbstractDataNodeAction(workbenchpartSite) { setText(tr("Show only selected nodes")); InitializeAction(); } QmitkDataNodeShowSelectedNodesAction::QmitkDataNodeShowSelectedNodesAction(QWidget* parent, berry::IWorkbenchPartSite* workbenchpartSite) : QAction(parent) , QmitkAbstractDataNodeAction(berry::IWorkbenchPartSite::Pointer(workbenchpartSite)) { setText(tr("Show only selected nodes")); InitializeAction(); } void QmitkDataNodeShowSelectedNodesAction::InitializeAction() { connect(this, &QmitkDataNodeShowSelectedNodesAction::triggered, this, &QmitkDataNodeShowSelectedNodesAction::OnActionTriggered); } void QmitkDataNodeShowSelectedNodesAction::OnActionTriggered(bool /*checked*/) { - if (m_DataStorage.IsExpired()) + auto dataStorage = m_DataStorage.Lock(); + + if (dataStorage.IsNull()) { return; } - auto dataStorage = m_DataStorage.Lock(); - mitk::BaseRenderer::Pointer baseRenderer = GetBaseRenderer(); auto selectedNodes = GetSelectedNodes(); auto allNodes = dataStorage->GetAll(); for (auto& node : *allNodes) { if (node.IsNotNull() && node->GetData() != nullptr && strcmp(node->GetData()->GetNameOfClass(), "PlaneGeometryData")) { node->SetVisibility(selectedNodes.contains(node), baseRenderer); } } if (nullptr == baseRenderer) { mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } else { mitk::RenderingManager::GetInstance()->RequestUpdate(baseRenderer->GetRenderWindow()); } } diff --git a/Plugins/org.mitk.gui.qt.application/src/QmitkDataNodeToggleVisibilityAction.cpp b/Plugins/org.mitk.gui.qt.application/src/QmitkDataNodeToggleVisibilityAction.cpp index c06967fb8e..a3eb2dc0ab 100644 --- a/Plugins/org.mitk.gui.qt.application/src/QmitkDataNodeToggleVisibilityAction.cpp +++ b/Plugins/org.mitk.gui.qt.application/src/QmitkDataNodeToggleVisibilityAction.cpp @@ -1,99 +1,103 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #include #include // mitk core #include // berry #include #include #include // namespace that contains the concrete action namespace ToggleVisibilityAction { void Run(berry::IWorkbenchPartSite::Pointer workbenchPartSite, mitk::DataStorage::Pointer dataStorage, const QList& selectedNodes /*= QList()*/, mitk::BaseRenderer* baseRenderer /*= nullptr*/) { bool isVisible; for (auto& node : selectedNodes) { if (node.IsNotNull()) { isVisible = false; node->GetBoolProperty("visible", isVisible, baseRenderer); node->SetVisibility(!isVisible, baseRenderer); } } berry::IPreferencesService* prefService = berry::Platform::GetPreferencesService(); berry::IPreferences::Pointer preferences = prefService->GetSystemPreferences()->Node(QmitkDataNodeGlobalReinitAction::ACTION_ID); bool globalReinit = preferences->GetBool("Call global reinit if node visibility is changed", false); if (globalReinit) { GlobalReinitAction::Run(workbenchPartSite, dataStorage); } else { if (nullptr == baseRenderer) { mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } else { mitk::RenderingManager::GetInstance()->RequestUpdate(baseRenderer->GetRenderWindow()); } } } } QmitkDataNodeToggleVisibilityAction::QmitkDataNodeToggleVisibilityAction(QWidget* parent, berry::IWorkbenchPartSite::Pointer workbenchpartSite) : QAction(parent) , QmitkAbstractDataNodeAction(workbenchpartSite) { setText(tr("Toggle visibility")); InitializeAction(); } QmitkDataNodeToggleVisibilityAction::QmitkDataNodeToggleVisibilityAction(QWidget* parent, berry::IWorkbenchPartSite* workbenchpartSite) : QAction(parent) , QmitkAbstractDataNodeAction(berry::IWorkbenchPartSite::Pointer(workbenchpartSite)) { setText(tr("Toggle visibility")); InitializeAction(); } void QmitkDataNodeToggleVisibilityAction::InitializeAction() { connect(this, &QmitkDataNodeToggleVisibilityAction::triggered, this, &QmitkDataNodeToggleVisibilityAction::OnActionTriggered); } void QmitkDataNodeToggleVisibilityAction::OnActionTriggered(bool /*checked*/) { - if (m_WorkbenchPartSite.Expired()) + auto workbenchPartSite = m_WorkbenchPartSite.Lock(); + + if (workbenchPartSite.IsNull()) { return; } - if (m_DataStorage.IsExpired()) + auto dataStorage = m_DataStorage.Lock(); + + if (dataStorage.IsNull()) { return; } mitk::BaseRenderer::Pointer baseRenderer = GetBaseRenderer(); auto dataNodes = GetSelectedNodes(); - ToggleVisibilityAction::Run(m_WorkbenchPartSite.Lock(), m_DataStorage.Lock(), dataNodes, baseRenderer); + ToggleVisibilityAction::Run(workbenchPartSite, dataStorage, dataNodes, baseRenderer); } diff --git a/Plugins/org.mitk.gui.qt.application/src/QmitkFileSaveAction.cpp b/Plugins/org.mitk.gui.qt.application/src/QmitkFileSaveAction.cpp index 2902c41168..d945baac54 100644 --- a/Plugins/org.mitk.gui.qt.application/src/QmitkFileSaveAction.cpp +++ b/Plugins/org.mitk.gui.qt.application/src/QmitkFileSaveAction.cpp @@ -1,273 +1,275 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #include "QmitkFileSaveAction.h" #include "internal/org_mitk_gui_qt_application_Activator.h" #include #include #include #include #include #include #include #include #include namespace { mitk::DataStorage::Pointer GetDataStorage() { auto context = mitk::org_mitk_gui_qt_application_Activator::GetContext(); if (nullptr == context) return nullptr; auto dataStorageServiceReference = context->getServiceReference(); if (!dataStorageServiceReference) return nullptr; auto dataStorageService = context->getService(dataStorageServiceReference); if (nullptr == dataStorageService) return nullptr; auto dataStorageReference = dataStorageService->GetDataStorage(); if (dataStorageReference.IsNull()) return nullptr; return dataStorageReference->GetDataStorage(); } QString GetParentPath(mitk::DataNode::Pointer dataNode) { if (dataNode.IsNull()) return ""; auto dataStorage = GetDataStorage(); if (dataStorage.IsNull()) return ""; auto sources = dataStorage->GetSources(dataNode); if (sources.IsNull() || sources->empty()) return ""; const auto &parentNode = sources->front(); if (parentNode.IsNull()) return ""; auto data = parentNode->GetData(); if (nullptr != data) { auto pathProperty = data->GetConstProperty("path"); if (pathProperty.IsNotNull()) return QFileInfo(QString::fromStdString(pathProperty->GetValueAsString())).canonicalPath(); } return GetParentPath(parentNode); } } class QmitkFileSaveActionPrivate { private: void HandleSelectionChanged(const berry::IWorkbenchPart::Pointer& /*part*/, const berry::ISelection::ConstPointer& selection) { this->SetEnabled(selection); } QScopedPointer m_SelectionListener; public: QmitkFileSaveActionPrivate() : m_SelectionListener(new berry::NullSelectionChangedAdapter( this, &QmitkFileSaveActionPrivate::HandleSelectionChanged)) { } ~QmitkFileSaveActionPrivate() { - if (!m_Window.Expired()) + auto window = m_Window.Lock(); + + if (window.IsNotNull()) { - m_Window.Lock()->GetSelectionService()->RemoveSelectionListener(m_SelectionListener.data()); + window->GetSelectionService()->RemoveSelectionListener(m_SelectionListener.data()); } } void Init(berry::IWorkbenchWindow* window, QAction* action) { m_Window = berry::IWorkbenchWindow::Pointer(window); m_Action = action; m_Action->setText("&Save..."); m_Action->setToolTip("Save data objects (images, surfaces,...)"); berry::ISelectionService* selectionService = m_Window.Lock()->GetSelectionService(); SetEnabled(selectionService->GetSelection()); selectionService->AddSelectionListener(m_SelectionListener.data()); QObject::connect(m_Action, SIGNAL(triggered(bool)), m_Action, SLOT(Run())); } berry::IPreferences::Pointer GetPreferences() const { berry::IPreferencesService* prefService = mitk::PluginActivator::GetInstance()->GetPreferencesService(); if (prefService != nullptr) { return prefService->GetSystemPreferences()->Node("/General"); } return berry::IPreferences::Pointer(nullptr); } QString GetLastFileSavePath() const { berry::IPreferences::Pointer prefs = GetPreferences(); if (prefs.IsNotNull()) { return prefs->Get("LastFileSavePath", ""); } return QString(); } void SetLastFileSavePath(const QString& path) const { berry::IPreferences::Pointer prefs = GetPreferences(); if (prefs.IsNotNull()) { prefs->Put("LastFileSavePath", path); prefs->Flush(); } } void SetEnabled(berry::ISelection::ConstPointer selection) { mitk::DataNodeSelection::ConstPointer nodeSelection = selection.Cast(); if (nodeSelection.IsNotNull() && !selection->IsEmpty()) { bool enable = false; std::list dataNodes = nodeSelection->GetSelectedDataNodes(); for (std::list::const_iterator nodeIter = dataNodes.begin(), nodeIterEnd = dataNodes.end(); nodeIter != nodeIterEnd; ++nodeIter) { if ((*nodeIter)->GetData() != nullptr) { enable = true; break; } } m_Action->setEnabled(enable); } else { m_Action->setEnabled(false); } } berry::IWorkbenchWindow::WeakPtr m_Window; QAction* m_Action; }; QmitkFileSaveAction::QmitkFileSaveAction(berry::IWorkbenchWindow::Pointer window) : QAction(tr("Save...")) , d(new QmitkFileSaveActionPrivate) { d->Init(window.GetPointer(), this); } QmitkFileSaveAction::QmitkFileSaveAction(const QIcon& icon, berry::IWorkbenchWindow::Pointer window) : QAction(tr("Save...")) , d(new QmitkFileSaveActionPrivate) { d->Init(window.GetPointer(), this); setIcon(icon); } QmitkFileSaveAction::QmitkFileSaveAction(const QIcon& icon, berry::IWorkbenchWindow* window) : QAction(tr("Save...")) , d(new QmitkFileSaveActionPrivate) { d->Init(window, this); setIcon(icon); } QmitkFileSaveAction::~QmitkFileSaveAction() { } void QmitkFileSaveAction::Run() { // get the list of selected base data objects mitk::DataNodeSelection::ConstPointer selection = d->m_Window.Lock()->GetSelectionService()->GetSelection().Cast(); if (selection.IsNull() || selection->IsEmpty()) { MITK_ERROR << "Assertion failed: data node selection is nullptr or empty"; return; } std::list dataNodes = selection->GetSelectedDataNodes(); std::vector data; QStringList names; for (std::list::const_iterator nodeIter = dataNodes.begin(), nodeIterEnd = dataNodes.end(); nodeIter != nodeIterEnd; ++nodeIter) { data.push_back((*nodeIter)->GetData()); std::string name; (*nodeIter)->GetStringProperty("name", name); names.push_back(QString::fromStdString(name)); } QString path; if (1 == data.size()) { if (nullptr != data[0]) { auto pathProperty = data[0]->GetConstProperty("path"); if (pathProperty.IsNotNull()) path = QFileInfo(QString::fromStdString(pathProperty->GetValueAsString())).canonicalPath(); } if (path.isEmpty()) path = GetParentPath(dataNodes.front()); } if (path.isEmpty()) path = d->GetLastFileSavePath(); try { auto setPathProperty = true; auto fileNames = QmitkIOUtil::Save(data, names, path, d->m_Action->parentWidget(), setPathProperty); if (!fileNames.empty()) d->SetLastFileSavePath(QFileInfo(fileNames.back()).absolutePath()); } catch (const mitk::Exception& e) { MITK_INFO << e; return; } } diff --git a/Plugins/org.mitk.gui.qt.common/src/QmitkNodeSelectionDialog.cpp b/Plugins/org.mitk.gui.qt.common/src/QmitkNodeSelectionDialog.cpp index 07bd9d18be..5c557c42bd 100644 --- a/Plugins/org.mitk.gui.qt.common/src/QmitkNodeSelectionDialog.cpp +++ b/Plugins/org.mitk.gui.qt.common/src/QmitkNodeSelectionDialog.cpp @@ -1,292 +1,293 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #include "QmitkNodeSelectionDialog.h" #include #include #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); m_CheckFunction = [](const NodeList &) { return ""; }; auto providers = mitk::DataStorageInspectorGenerator::GetProviders(); auto visibleProviders = mitk::GetVisibleDataStorageInspectors(); auto preferredID = mitk::GetPreferredDataStorageInspector(); if (visibleProviders.empty()) { MITK_DEBUG << "No presets for visible node selection inspectors available. Use fallback (show all available inspectors)"; unsigned int order = 0; for (const auto &proIter : providers) { visibleProviders.insert(std::make_pair(order, proIter.first)); ++order; } } int preferredIndex = 0; bool preferredFound = false; for (const auto &proIter : visibleProviders) { auto finding = providers.find(proIter.second); if (finding != providers.end()) { if (finding->second->GetInspectorID() != QmitkDataStorageFavoriteNodesInspector::INSPECTOR_ID() && finding->second->GetInspectorID() != QmitkDataStorageSelectionHistoryInspector::INSPECTOR_ID()) { auto provider = finding->second; this->AddPanel(provider, preferredID, preferredFound, preferredIndex); } } else { MITK_DEBUG << "No provider registered for inspector that is defined as visible in the preferences. Illegal inspector ID: " << proIter.second; } } if (mitk::GetShowFavoritesInspector()) { auto favoritesPorvider = mitk::DataStorageInspectorGenerator::GetProvider(QmitkDataStorageFavoriteNodesInspector::INSPECTOR_ID()); if (favoritesPorvider != nullptr) { this->AddPanel(favoritesPorvider, preferredID, preferredFound, preferredIndex); } } if (mitk::GetShowHistoryInspector()) { auto historyPorvider = mitk::DataStorageInspectorGenerator::GetProvider(QmitkDataStorageSelectionHistoryInspector::INSPECTOR_ID()); if (historyPorvider != nullptr) { this->AddPanel(historyPorvider, preferredID, preferredFound, preferredIndex); } } m_Controls.tabWidget->setCurrentIndex(preferredIndex); this->setWindowTitle(title); this->setToolTip(hint); m_Controls.hint->setText(hint); m_Controls.hint->setVisible(!hint.isEmpty()); if(hint.isEmpty()) { m_Controls.layoutHint->setContentsMargins(0, 0, 0, 0); } else { m_Controls.layoutHint->setContentsMargins(6, 6, 6, 6); } this->SetErrorText(""); m_Controls.btnAddToFav->setIcon(berry::QtStyleManager::ThemeIcon(QStringLiteral(":/Qmitk/favorite_add.svg"))); connect(m_Controls.btnAddToFav, &QPushButton::clicked, this, &QmitkNodeSelectionDialog::OnFavoriteNodesButtonClicked); connect(m_Controls.buttonBox, &QDialogButtonBox::accepted, this, &QmitkNodeSelectionDialog::OnOK); connect(m_Controls.buttonBox, &QDialogButtonBox::rejected, this, &QmitkNodeSelectionDialog::OnCancel); } void QmitkNodeSelectionDialog::SetDataStorage(mitk::DataStorage* dataStorage) { if (m_DataStorage != dataStorage) { m_DataStorage = dataStorage; + auto lockedDataStorage = m_DataStorage.Lock(); - if (!m_DataStorage.IsExpired()) + if (lockedDataStorage.IsNotNull()) { for (auto panel : m_Panels) { - panel->SetDataStorage(dataStorage); + panel->SetDataStorage(lockedDataStorage); } } } } void QmitkNodeSelectionDialog::SetNodePredicate(const mitk::NodePredicateBase* nodePredicate) { if (m_NodePredicate != nodePredicate) { m_NodePredicate = nodePredicate; for (auto panel : m_Panels) { panel->SetNodePredicate(m_NodePredicate); } } } const mitk::NodePredicateBase* QmitkNodeSelectionDialog::GetNodePredicate() const { return m_NodePredicate; } QmitkNodeSelectionDialog::NodeList QmitkNodeSelectionDialog::GetSelectedNodes() const { return m_SelectedNodes; } void QmitkNodeSelectionDialog::SetSelectionCheckFunction(const SelectionCheckFunctionType &checkFunction) { m_CheckFunction = checkFunction; auto checkResponse = m_CheckFunction(m_SelectedNodes); SetErrorText(checkResponse); m_Controls.buttonBox->button(QDialogButtonBox::Ok)->setEnabled(checkResponse.empty()); } void QmitkNodeSelectionDialog::SetErrorText(const std::string& checkResponse) { m_Controls.error->setText(QString::fromStdString(checkResponse)); m_Controls.error->setVisible(!checkResponse.empty()); if (checkResponse.empty()) { m_Controls.layoutError->setContentsMargins(0, 0, 0, 0); } else { m_Controls.layoutError->setContentsMargins(6, 6, 6, 6); } } bool QmitkNodeSelectionDialog::GetSelectOnlyVisibleNodes() const { return m_SelectOnlyVisibleNodes; } void QmitkNodeSelectionDialog::SetSelectionMode(SelectionMode mode) { m_SelectionMode = mode; for (auto panel : m_Panels) { panel->SetSelectionMode(mode); } } QmitkNodeSelectionDialog::SelectionMode QmitkNodeSelectionDialog::GetSelectionMode() const { return m_SelectionMode; } 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; auto checkResponse = m_CheckFunction(m_SelectedNodes); SetErrorText(checkResponse); m_Controls.buttonBox->button(QDialogButtonBox::Ok)->setEnabled(checkResponse.empty()); for (auto panel : m_Panels) { panel->SetCurrentSelection(selectedNodes); } } void QmitkNodeSelectionDialog::OnSelectionChanged(NodeList selectedNodes) { SetCurrentSelection(selectedNodes); emit CurrentSelectionChanged(selectedNodes); } void QmitkNodeSelectionDialog::OnFavoriteNodesButtonClicked() { for (auto node : qAsConst(m_SelectedNodes)) { node->SetBoolProperty("org.mitk.selection.favorite", true); } } void QmitkNodeSelectionDialog::OnOK() { for (const auto &node : qAsConst(m_SelectedNodes)) { QmitkDataStorageSelectionHistoryInspector::AddNodeToHistory(node); } this->accept(); } void QmitkNodeSelectionDialog::OnCancel() { this->reject(); } void QmitkNodeSelectionDialog::AddPanel(const mitk::IDataStorageInspectorProvider * provider, const mitk::IDataStorageInspectorProvider::InspectorIDType& preferredID, bool &preferredFound, int &preferredIndex) { auto inspector = provider->CreateInspector(); QString name = QString::fromStdString(provider->GetInspectorDisplayName()); QString desc = QString::fromStdString(provider->GetInspectorDescription()); inspector->setParent(this); inspector->SetSelectionMode(m_SelectionMode); auto tabPanel = new QWidget(); tabPanel->setObjectName(QString("tab_") + name); tabPanel->setToolTip(desc); auto verticalLayout = new QVBoxLayout(tabPanel); verticalLayout->setSpacing(0); verticalLayout->setContentsMargins(0, 0, 0, 0); verticalLayout->addWidget(inspector); auto panelPos = m_Controls.tabWidget->insertTab(m_Controls.tabWidget->count(), tabPanel, name); auto icon = provider->GetInspectorIcon(); if (!icon.isNull()) { m_Controls.tabWidget->setTabIcon(panelPos, icon); } m_Panels.push_back(inspector); connect(inspector, &QmitkAbstractDataStorageInspector::CurrentSelectionChanged, this, &QmitkNodeSelectionDialog::OnSelectionChanged); connect(inspector->GetView(), &QAbstractItemView::doubleClicked, this, &QmitkNodeSelectionDialog::OnDoubleClicked); preferredFound = preferredFound || provider->GetInspectorID() == preferredID; if (!preferredFound) { ++preferredIndex; } } void QmitkNodeSelectionDialog::OnDoubleClicked(const QModelIndex& /*index*/) { const auto isOK = m_Controls.buttonBox->button(QDialogButtonBox::Ok)->isEnabled(); if (!m_SelectedNodes.empty() && isOK) { this->OnOK(); } } diff --git a/Plugins/org.mitk.gui.qt.datamanager/src/internal/QmitkNodeTableViewKeyFilter.cpp b/Plugins/org.mitk.gui.qt.datamanager/src/internal/QmitkNodeTableViewKeyFilter.cpp index 3360f2b276..59568929bb 100644 --- a/Plugins/org.mitk.gui.qt.datamanager/src/internal/QmitkNodeTableViewKeyFilter.cpp +++ b/Plugins/org.mitk.gui.qt.datamanager/src/internal/QmitkNodeTableViewKeyFilter.cpp @@ -1,116 +1,116 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #include "QmitkNodeTableViewKeyFilter.h" #include "../QmitkDataManagerView.h" // mitk gui qt application plugin #include #include #include #include #include #include #include "berryIPreferencesService.h" #include "berryPlatform.h" // qt #include #include QmitkNodeTableViewKeyFilter::QmitkNodeTableViewKeyFilter(QObject *dataManagerView, mitk::DataStorage *dataStorage) : QObject(dataManagerView), m_DataStorage(dataStorage) { m_PreferencesService = berry::Platform::GetPreferencesService(); } bool QmitkNodeTableViewKeyFilter::eventFilter(QObject *obj, QEvent *event) { - if (m_DataStorage.IsExpired()) + auto dataStorage = m_DataStorage.Lock(); + + if (dataStorage.IsNull()) { // standard event processing return QObject::eventFilter(obj, event); } - auto dataStorage = m_DataStorage.Lock(); - QmitkDataManagerView *dataManagerView = qobject_cast(this->parent()); if (event->type() == QEvent::KeyPress && dataManagerView) { berry::IPreferences::Pointer nodeTableKeyPrefs = m_PreferencesService->GetSystemPreferences()->Node("/DataManager/Hotkeys"); QKeySequence makeAllInvisible = QKeySequence(nodeTableKeyPrefs->Get("Make all nodes invisible", "Ctrl+V")); QKeySequence toggleVisibility = QKeySequence(nodeTableKeyPrefs->Get("Toggle visibility of selected nodes", "V")); QKeySequence deleteSelectedNodes = QKeySequence(nodeTableKeyPrefs->Get("Delete selected nodes", "Del")); QKeySequence reinit = QKeySequence(nodeTableKeyPrefs->Get("Reinit selected nodes", "R")); QKeySequence globalReinit = QKeySequence(nodeTableKeyPrefs->Get("Global reinit", "Ctrl+R")); QKeySequence showInfo = QKeySequence(nodeTableKeyPrefs->Get("Show node information", "Ctrl+I")); QKeyEvent *keyEvent = static_cast(event); QKeySequence keySequence = QKeySequence(keyEvent->modifiers() + keyEvent->key()); // if no modifier was pressed the sequence is now empty if (keySequence.isEmpty()) { keySequence = QKeySequence(keyEvent->key()); } auto selectedNodes = AbstractDataNodeAction::GetSelectedNodes(dataManagerView->GetSite()); if (keySequence == makeAllInvisible) { if (selectedNodes.empty()) { // if no nodes are selected, hide all nodes of the data storage auto nodeset = dataStorage->GetAll(); for (auto it = nodeset->Begin(); it != nodeset->End(); ++it) { mitk::DataNode* node = it->Value(); if (nullptr != node) { selectedNodes.push_back(node); } } } HideAllAction::Run(selectedNodes); return true; } if (keySequence == deleteSelectedNodes) { RemoveAction::Run(dataManagerView->GetSite(), dataStorage, selectedNodes); return true; } if (keySequence == toggleVisibility) { ToggleVisibilityAction::Run(dataManagerView->GetSite(), dataStorage, selectedNodes); return true; } if (keySequence == reinit) { ReinitAction::Run(dataManagerView->GetSite(), dataStorage, selectedNodes); return true; } if (keySequence == globalReinit) { GlobalReinitAction::Run(dataManagerView->GetSite(), dataStorage); return true; } if (keySequence == showInfo) { ShowDetailsAction::Run(selectedNodes); return true; } } // standard event processing return QObject::eventFilter(obj, event); } diff --git a/Plugins/org.mitk.gui.qt.ext/src/QmitkExtWorkbenchWindowAdvisor.cpp b/Plugins/org.mitk.gui.qt.ext/src/QmitkExtWorkbenchWindowAdvisor.cpp index ea07102a60..1ff5df4acd 100644 --- a/Plugins/org.mitk.gui.qt.ext/src/QmitkExtWorkbenchWindowAdvisor.cpp +++ b/Plugins/org.mitk.gui.qt.ext/src/QmitkExtWorkbenchWindowAdvisor.cpp @@ -1,1431 +1,1437 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #include "QmitkExtWorkbenchWindowAdvisor.h" #include "QmitkExtActionBarAdvisor.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include // UGLYYY #include "internal/QmitkExtWorkbenchWindowAdvisorHack.h" #include "internal/QmitkCommonExtPlugin.h" #include "mitkUndoController.h" #include "mitkVerboseLimitedLinearUndo.h" #include #include #include #include #include #include QmitkExtWorkbenchWindowAdvisorHack* QmitkExtWorkbenchWindowAdvisorHack::undohack = new QmitkExtWorkbenchWindowAdvisorHack(); QString QmitkExtWorkbenchWindowAdvisor::QT_SETTINGS_FILENAME = "QtSettings.ini"; static bool USE_EXPERIMENTAL_COMMAND_CONTRIBUTIONS = false; class PartListenerForTitle: public berry::IPartListener { public: PartListenerForTitle(QmitkExtWorkbenchWindowAdvisor* wa) : windowAdvisor(wa) { } Events::Types GetPartEventTypes() const override { return Events::ACTIVATED | Events::BROUGHT_TO_TOP | Events::CLOSED | Events::HIDDEN | Events::VISIBLE; } void PartActivated(const berry::IWorkbenchPartReference::Pointer& ref) override { if (ref.Cast ()) { windowAdvisor->UpdateTitle(false); } } void PartBroughtToTop(const berry::IWorkbenchPartReference::Pointer& ref) override { if (ref.Cast ()) { windowAdvisor->UpdateTitle(false); } } void PartClosed(const berry::IWorkbenchPartReference::Pointer& /*ref*/) override { windowAdvisor->UpdateTitle(false); } void PartHidden(const berry::IWorkbenchPartReference::Pointer& ref) override { - if (!windowAdvisor->lastActiveEditor.Expired() && - ref->GetPart(false) == windowAdvisor->lastActiveEditor.Lock()) + auto lockedLastActiveEditor = windowAdvisor->lastActiveEditor.Lock(); + + if (lockedLastActiveEditor.IsNotNull() && ref->GetPart(false) == lockedLastActiveEditor) { windowAdvisor->UpdateTitle(true); } } void PartVisible(const berry::IWorkbenchPartReference::Pointer& ref) override { - if (!windowAdvisor->lastActiveEditor.Expired() && - ref->GetPart(false) == windowAdvisor->lastActiveEditor.Lock()) + auto lockedLastActiveEditor = windowAdvisor->lastActiveEditor.Lock(); + + if (lockedLastActiveEditor.IsNotNull() && ref->GetPart(false) == lockedLastActiveEditor) { windowAdvisor->UpdateTitle(false); } } private: QmitkExtWorkbenchWindowAdvisor* windowAdvisor; }; class PartListenerForViewNavigator: public berry::IPartListener { public: PartListenerForViewNavigator(QAction* act) : viewNavigatorAction(act) { } Events::Types GetPartEventTypes() const override { return Events::OPENED | Events::CLOSED | Events::HIDDEN | Events::VISIBLE; } void PartOpened(const berry::IWorkbenchPartReference::Pointer& ref) override { if (ref->GetId()=="org.mitk.views.viewnavigator") { viewNavigatorAction->setChecked(true); } } void PartClosed(const berry::IWorkbenchPartReference::Pointer& ref) override { if (ref->GetId()=="org.mitk.views.viewnavigator") { viewNavigatorAction->setChecked(false); } } void PartVisible(const berry::IWorkbenchPartReference::Pointer& ref) override { if (ref->GetId()=="org.mitk.views.viewnavigator") { viewNavigatorAction->setChecked(true); } } void PartHidden(const berry::IWorkbenchPartReference::Pointer& ref) override { if (ref->GetId()=="org.mitk.views.viewnavigator") { viewNavigatorAction->setChecked(false); } } private: QAction* viewNavigatorAction; }; class PartListenerForImageNavigator: public berry::IPartListener { public: PartListenerForImageNavigator(QAction* act) : imageNavigatorAction(act) { } Events::Types GetPartEventTypes() const override { return Events::OPENED | Events::CLOSED | Events::HIDDEN | Events::VISIBLE; } void PartOpened(const berry::IWorkbenchPartReference::Pointer& ref) override { if (ref->GetId()=="org.mitk.views.imagenavigator") { imageNavigatorAction->setChecked(true); } } void PartClosed(const berry::IWorkbenchPartReference::Pointer& ref) override { if (ref->GetId()=="org.mitk.views.imagenavigator") { imageNavigatorAction->setChecked(false); } } void PartVisible(const berry::IWorkbenchPartReference::Pointer& ref) override { if (ref->GetId()=="org.mitk.views.imagenavigator") { imageNavigatorAction->setChecked(true); } } void PartHidden(const berry::IWorkbenchPartReference::Pointer& ref) override { if (ref->GetId()=="org.mitk.views.imagenavigator") { imageNavigatorAction->setChecked(false); } } private: QAction* imageNavigatorAction; }; class PerspectiveListenerForTitle: public berry::IPerspectiveListener { public: PerspectiveListenerForTitle(QmitkExtWorkbenchWindowAdvisor* wa) : windowAdvisor(wa) , perspectivesClosed(false) { } Events::Types GetPerspectiveEventTypes() const override { if (USE_EXPERIMENTAL_COMMAND_CONTRIBUTIONS) { return Events::ACTIVATED | Events::SAVED_AS | Events::DEACTIVATED; } else { return Events::ACTIVATED | Events::SAVED_AS | Events::DEACTIVATED | Events::CLOSED | Events::OPENED; } } void PerspectiveActivated(const berry::IWorkbenchPage::Pointer& /*page*/, const berry::IPerspectiveDescriptor::Pointer& /*perspective*/) override { windowAdvisor->UpdateTitle(false); } void PerspectiveSavedAs(const berry::IWorkbenchPage::Pointer& /*page*/, const berry::IPerspectiveDescriptor::Pointer& /*oldPerspective*/, const berry::IPerspectiveDescriptor::Pointer& /*newPerspective*/) override { windowAdvisor->UpdateTitle(false); } void PerspectiveDeactivated(const berry::IWorkbenchPage::Pointer& /*page*/, const berry::IPerspectiveDescriptor::Pointer& /*perspective*/) override { windowAdvisor->UpdateTitle(false); } void PerspectiveOpened(const berry::IWorkbenchPage::Pointer& /*page*/, const berry::IPerspectiveDescriptor::Pointer& /*perspective*/) override { if (perspectivesClosed) { QListIterator i(windowAdvisor->viewActions); while (i.hasNext()) { i.next()->setEnabled(true); } //GetViewRegistry()->Find("org.mitk.views.imagenavigator"); if(windowAdvisor->GetWindowConfigurer()->GetWindow()->GetWorkbench()->GetEditorRegistry()->FindEditor("org.mitk.editors.dicombrowser")) { windowAdvisor->openDicomEditorAction->setEnabled(true); } if (windowAdvisor->GetWindowConfigurer()->GetWindow()->GetWorkbench()->GetEditorRegistry()->FindEditor("org.mitk.editors.stdmultiwidget")) { windowAdvisor->openStdMultiWidgetEditorAction->setEnabled(true); } if (windowAdvisor->GetWindowConfigurer()->GetWindow()->GetWorkbench()->GetEditorRegistry()->FindEditor("org.mitk.editors.mxnmultiwidget")) { windowAdvisor->openMxNMultiWidgetEditorAction->setEnabled(true); } windowAdvisor->fileSaveProjectAction->setEnabled(true); windowAdvisor->closeProjectAction->setEnabled(true); windowAdvisor->undoAction->setEnabled(true); windowAdvisor->redoAction->setEnabled(true); windowAdvisor->imageNavigatorAction->setEnabled(true); windowAdvisor->viewNavigatorAction->setEnabled(true); windowAdvisor->resetPerspAction->setEnabled(true); if( windowAdvisor->GetShowClosePerspectiveMenuItem() ) { windowAdvisor->closePerspAction->setEnabled(true); } } perspectivesClosed = false; } void PerspectiveClosed(const berry::IWorkbenchPage::Pointer& /*page*/, const berry::IPerspectiveDescriptor::Pointer& /*perspective*/) override { berry::IWorkbenchWindow::Pointer wnd = windowAdvisor->GetWindowConfigurer()->GetWindow(); bool allClosed = true; if (wnd->GetActivePage()) { QList perspectives(wnd->GetActivePage()->GetOpenPerspectives()); allClosed = perspectives.empty(); } if (allClosed) { perspectivesClosed = true; QListIterator i(windowAdvisor->viewActions); while (i.hasNext()) { i.next()->setEnabled(false); } if(windowAdvisor->GetWindowConfigurer()->GetWindow()->GetWorkbench()->GetEditorRegistry()->FindEditor("org.mitk.editors.dicombrowser")) { windowAdvisor->openDicomEditorAction->setEnabled(false); } if (windowAdvisor->GetWindowConfigurer()->GetWindow()->GetWorkbench()->GetEditorRegistry()->FindEditor("org.mitk.editors.stdmultiwidget")) { windowAdvisor->openStdMultiWidgetEditorAction->setEnabled(false); } if (windowAdvisor->GetWindowConfigurer()->GetWindow()->GetWorkbench()->GetEditorRegistry()->FindEditor("org.mitk.editors.mxnmultiwidget")) { windowAdvisor->openMxNMultiWidgetEditorAction->setEnabled(false); } windowAdvisor->fileSaveProjectAction->setEnabled(false); windowAdvisor->closeProjectAction->setEnabled(false); windowAdvisor->undoAction->setEnabled(false); windowAdvisor->redoAction->setEnabled(false); windowAdvisor->imageNavigatorAction->setEnabled(false); windowAdvisor->viewNavigatorAction->setEnabled(false); windowAdvisor->resetPerspAction->setEnabled(false); if( windowAdvisor->GetShowClosePerspectiveMenuItem() ) { windowAdvisor->closePerspAction->setEnabled(false); } } } private: QmitkExtWorkbenchWindowAdvisor* windowAdvisor; bool perspectivesClosed; }; class PerspectiveListenerForMenu: public berry::IPerspectiveListener { public: PerspectiveListenerForMenu(QmitkExtWorkbenchWindowAdvisor* wa) : windowAdvisor(wa) { } Events::Types GetPerspectiveEventTypes() const override { return Events::ACTIVATED | Events::DEACTIVATED; } void PerspectiveActivated(const berry::IWorkbenchPage::Pointer& /*page*/, const berry::IPerspectiveDescriptor::Pointer& perspective) override { QAction* action = windowAdvisor->mapPerspIdToAction[perspective->GetId()]; if (action) { action->setChecked(true); } } void PerspectiveDeactivated(const berry::IWorkbenchPage::Pointer& /*page*/, const berry::IPerspectiveDescriptor::Pointer& perspective) override { QAction* action = windowAdvisor->mapPerspIdToAction[perspective->GetId()]; if (action) { action->setChecked(false); } } private: QmitkExtWorkbenchWindowAdvisor* windowAdvisor; }; QmitkExtWorkbenchWindowAdvisor::QmitkExtWorkbenchWindowAdvisor(berry::WorkbenchAdvisor* wbAdvisor, berry::IWorkbenchWindowConfigurer::Pointer configurer) : berry::WorkbenchWindowAdvisor(configurer) , lastInput(nullptr) , wbAdvisor(wbAdvisor) , showViewToolbar(true) , showPerspectiveToolbar(false) , showVersionInfo(true) , showMitkVersionInfo(true) , showViewMenuItem(true) , showNewWindowMenuItem(false) , showClosePerspectiveMenuItem(true) , viewNavigatorFound(false) , showMemoryIndicator(true) , dropTargetListener(new QmitkDefaultDropTargetListener) { productName = QCoreApplication::applicationName(); viewExcludeList.push_back("org.mitk.views.viewnavigator"); } QmitkExtWorkbenchWindowAdvisor::~QmitkExtWorkbenchWindowAdvisor() { } berry::ActionBarAdvisor::Pointer QmitkExtWorkbenchWindowAdvisor::CreateActionBarAdvisor(berry::IActionBarConfigurer::Pointer configurer) { if (USE_EXPERIMENTAL_COMMAND_CONTRIBUTIONS) { berry::ActionBarAdvisor::Pointer actionBarAdvisor(new QmitkExtActionBarAdvisor(configurer)); return actionBarAdvisor; } else { return berry::WorkbenchWindowAdvisor::CreateActionBarAdvisor(configurer); } } QWidget* QmitkExtWorkbenchWindowAdvisor::CreateEmptyWindowContents(QWidget* parent) { QWidget* parentWidget = static_cast(parent); auto label = new QLabel(parentWidget); label->setText("No perspectives are open. Open a perspective in the Window->Open Perspective menu."); label->setContentsMargins(10,10,10,10); label->setAlignment(Qt::AlignTop); label->setEnabled(false); parentWidget->layout()->addWidget(label); return label; } void QmitkExtWorkbenchWindowAdvisor::ShowClosePerspectiveMenuItem(bool show) { showClosePerspectiveMenuItem = show; } bool QmitkExtWorkbenchWindowAdvisor::GetShowClosePerspectiveMenuItem() { return showClosePerspectiveMenuItem; } void QmitkExtWorkbenchWindowAdvisor::ShowMemoryIndicator(bool show) { showMemoryIndicator = show; } bool QmitkExtWorkbenchWindowAdvisor::GetShowMemoryIndicator() { return showMemoryIndicator; } void QmitkExtWorkbenchWindowAdvisor::ShowNewWindowMenuItem(bool show) { showNewWindowMenuItem = show; } void QmitkExtWorkbenchWindowAdvisor::ShowViewToolbar(bool show) { showViewToolbar = show; } void QmitkExtWorkbenchWindowAdvisor::ShowViewMenuItem(bool show) { showViewMenuItem = show; } void QmitkExtWorkbenchWindowAdvisor::ShowPerspectiveToolbar(bool show) { showPerspectiveToolbar = show; } void QmitkExtWorkbenchWindowAdvisor::ShowVersionInfo(bool show) { showVersionInfo = show; } void QmitkExtWorkbenchWindowAdvisor::ShowMitkVersionInfo(bool show) { showMitkVersionInfo = show; } void QmitkExtWorkbenchWindowAdvisor::SetProductName(const QString& product) { productName = product; } void QmitkExtWorkbenchWindowAdvisor::SetWindowIcon(const QString& wndIcon) { windowIcon = wndIcon; } void QmitkExtWorkbenchWindowAdvisor::PostWindowCreate() { // very bad hack... berry::IWorkbenchWindow::Pointer window = this->GetWindowConfigurer()->GetWindow(); QMainWindow* mainWindow = qobject_cast (window->GetShell()->GetControl()); if (!windowIcon.isEmpty()) { mainWindow->setWindowIcon(QIcon(windowIcon)); } mainWindow->setContextMenuPolicy(Qt::PreventContextMenu); // Load icon theme QIcon::setThemeSearchPaths(QStringList() << QStringLiteral(":/org_mitk_icons/icons/")); QIcon::setThemeName(QStringLiteral("awesome")); // ==== Application menu ============================ QMenuBar* menuBar = mainWindow->menuBar(); menuBar->setContextMenuPolicy(Qt::PreventContextMenu); #ifdef __APPLE__ menuBar->setNativeMenuBar(true); #else menuBar->setNativeMenuBar(false); #endif auto basePath = QStringLiteral(":/org_mitk_icons/icons/awesome/scalable/actions/"); auto fileOpenAction = new QmitkFileOpenAction(berry::QtStyleManager::ThemeIcon(basePath + "document-open.svg"), window); fileOpenAction->setShortcut(QKeySequence::Open); auto fileSaveAction = new QmitkFileSaveAction(berry::QtStyleManager::ThemeIcon(basePath + "document-save.svg"), window); fileSaveAction->setShortcut(QKeySequence::Save); fileSaveProjectAction = new QmitkExtFileSaveProjectAction(window); fileSaveProjectAction->setIcon(berry::QtStyleManager::ThemeIcon(basePath + "document-save.svg")); closeProjectAction = new QmitkCloseProjectAction(window); closeProjectAction->setIcon(berry::QtStyleManager::ThemeIcon(basePath + "edit-delete.svg")); auto perspGroup = new QActionGroup(menuBar); std::map VDMap; // sort elements (converting vector to map...) QList::const_iterator iter; berry::IViewRegistry* viewRegistry = berry::PlatformUI::GetWorkbench()->GetViewRegistry(); const QList viewDescriptors = viewRegistry->GetViews(); bool skip = false; for (iter = viewDescriptors.begin(); iter != viewDescriptors.end(); ++iter) { // if viewExcludeList is set, it contains the id-strings of view, which // should not appear as an menu-entry in the menu if (viewExcludeList.size() > 0) { for (int i=0; iGetId()) { skip = true; break; } } if (skip) { skip = false; continue; } } if ((*iter)->GetId() == "org.blueberry.ui.internal.introview") continue; if ((*iter)->GetId() == "org.mitk.views.imagenavigator") continue; if ((*iter)->GetId() == "org.mitk.views.viewnavigator") continue; std::pair p((*iter)->GetLabel(), (*iter)); VDMap.insert(p); } std::map::const_iterator MapIter; for (MapIter = VDMap.begin(); MapIter != VDMap.end(); ++MapIter) { berry::QtShowViewAction* viewAction = new berry::QtShowViewAction(window, (*MapIter).second); viewActions.push_back(viewAction); } if (!USE_EXPERIMENTAL_COMMAND_CONTRIBUTIONS) { QMenu* fileMenu = menuBar->addMenu("&File"); fileMenu->setObjectName("FileMenu"); fileMenu->addAction(fileOpenAction); fileMenu->addAction(fileSaveAction); fileMenu->addAction(fileSaveProjectAction); fileMenu->addAction(closeProjectAction); fileMenu->addSeparator(); QAction* fileExitAction = new QmitkFileExitAction(window); fileExitAction->setIcon(berry::QtStyleManager::ThemeIcon(basePath + "system-log-out.svg")); fileExitAction->setShortcut(QKeySequence::Quit); fileExitAction->setObjectName("QmitkFileExitAction"); fileMenu->addAction(fileExitAction); // another bad hack to get an edit/undo menu... QMenu* editMenu = menuBar->addMenu("&Edit"); undoAction = editMenu->addAction(berry::QtStyleManager::ThemeIcon(basePath + "edit-undo.svg"), "&Undo", QmitkExtWorkbenchWindowAdvisorHack::undohack, SLOT(onUndo()), QKeySequence("CTRL+Z")); undoAction->setToolTip("Undo the last action (not supported by all modules)"); redoAction = editMenu->addAction(berry::QtStyleManager::ThemeIcon(basePath + "edit-redo.svg"), "&Redo", QmitkExtWorkbenchWindowAdvisorHack::undohack, SLOT(onRedo()), QKeySequence("CTRL+Y")); redoAction->setToolTip("execute the last action that was undone again (not supported by all modules)"); // ==== Window Menu ========================== QMenu* windowMenu = menuBar->addMenu("Window"); if (showNewWindowMenuItem) { windowMenu->addAction("&New Window", QmitkExtWorkbenchWindowAdvisorHack::undohack, SLOT(onNewWindow())); windowMenu->addSeparator(); } QMenu* perspMenu = windowMenu->addMenu("&Open Perspective"); QMenu* viewMenu = nullptr; if (showViewMenuItem) { viewMenu = windowMenu->addMenu("Show &View"); viewMenu->setObjectName("Show View"); } windowMenu->addSeparator(); resetPerspAction = windowMenu->addAction("&Reset Perspective", QmitkExtWorkbenchWindowAdvisorHack::undohack, SLOT(onResetPerspective())); if(showClosePerspectiveMenuItem) closePerspAction = windowMenu->addAction("&Close Perspective", QmitkExtWorkbenchWindowAdvisorHack::undohack, SLOT(onClosePerspective())); windowMenu->addSeparator(); windowMenu->addAction("&Preferences...", QmitkExtWorkbenchWindowAdvisorHack::undohack, SLOT(onEditPreferences()), QKeySequence("CTRL+P")); // fill perspective menu berry::IPerspectiveRegistry* perspRegistry = window->GetWorkbench()->GetPerspectiveRegistry(); QList perspectives( perspRegistry->GetPerspectives()); skip = false; for (QList::iterator perspIt = perspectives.begin(); perspIt != perspectives.end(); ++perspIt) { // if perspectiveExcludeList is set, it contains the id-strings of perspectives, which // should not appear as an menu-entry in the perspective menu if (perspectiveExcludeList.size() > 0) { for (int i=0; iGetId()) { skip = true; break; } } if (skip) { skip = false; continue; } } QAction* perspAction = new berry::QtOpenPerspectiveAction(window, *perspIt, perspGroup); mapPerspIdToAction.insert((*perspIt)->GetId(), perspAction); } perspMenu->addActions(perspGroup->actions()); if (showViewMenuItem) { for (auto viewAction : qAsConst(viewActions)) { viewMenu->addAction(viewAction); } } // ===== Help menu ==================================== QMenu* helpMenu = menuBar->addMenu("&Help"); helpMenu->addAction("&Welcome",this, SLOT(onIntro())); helpMenu->addAction("&Open Help Perspective", this, SLOT(onHelpOpenHelpPerspective())); helpMenu->addAction("&Context Help",this, SLOT(onHelp()), QKeySequence("F1")); helpMenu->addAction("&About",this, SLOT(onAbout())); // ===================================================== } else { undoAction = new QmitkUndoAction(berry::QtStyleManager::ThemeIcon(basePath + "edit-undo.svg"), nullptr); undoAction->setShortcut(QKeySequence::Undo); redoAction = new QmitkRedoAction(berry::QtStyleManager::ThemeIcon(basePath + "edit-redo.svg"), nullptr); redoAction->setShortcut(QKeySequence::Redo); } // toolbar for showing file open, undo, redo and other main actions auto mainActionsToolBar = new QToolBar; mainActionsToolBar->setObjectName("mainActionsToolBar"); mainActionsToolBar->setContextMenuPolicy(Qt::PreventContextMenu); #ifdef __APPLE__ mainActionsToolBar->setToolButtonStyle ( Qt::ToolButtonTextUnderIcon ); #else mainActionsToolBar->setToolButtonStyle ( Qt::ToolButtonTextBesideIcon ); #endif basePath = QStringLiteral(":/org.mitk.gui.qt.ext/"); imageNavigatorAction = new QAction(berry::QtStyleManager::ThemeIcon(basePath + "image_navigator.svg"), "&Image Navigator", nullptr); bool imageNavigatorViewFound = window->GetWorkbench()->GetViewRegistry()->Find("org.mitk.views.imagenavigator"); if (this->GetWindowConfigurer()->GetWindow()->GetWorkbench()->GetEditorRegistry()->FindEditor("org.mitk.editors.dicombrowser")) { openDicomEditorAction = new QmitkOpenDicomEditorAction(berry::QtStyleManager::ThemeIcon(basePath + "dicom.svg"), window); } if (this->GetWindowConfigurer()->GetWindow()->GetWorkbench()->GetEditorRegistry()->FindEditor("org.mitk.editors.stdmultiwidget")) { openStdMultiWidgetEditorAction = new QmitkOpenStdMultiWidgetEditorAction(QIcon(":/org.mitk.gui.qt.ext/Editor.png"), window); } if (this->GetWindowConfigurer()->GetWindow()->GetWorkbench()->GetEditorRegistry()->FindEditor("org.mitk.editors.mxnmultiwidget")) { openMxNMultiWidgetEditorAction = new QmitkOpenMxNMultiWidgetEditorAction(QIcon(":/org.mitk.gui.qt.ext/Editor.png"), window); } if (imageNavigatorViewFound) { QObject::connect(imageNavigatorAction, SIGNAL(triggered(bool)), QmitkExtWorkbenchWindowAdvisorHack::undohack, SLOT(onImageNavigator())); imageNavigatorAction->setCheckable(true); // add part listener for image navigator imageNavigatorPartListener.reset(new PartListenerForImageNavigator(imageNavigatorAction)); window->GetPartService()->AddPartListener(imageNavigatorPartListener.data()); berry::IViewPart::Pointer imageNavigatorView = window->GetActivePage()->FindView("org.mitk.views.imagenavigator"); imageNavigatorAction->setChecked(false); if (imageNavigatorView) { bool isImageNavigatorVisible = window->GetActivePage()->IsPartVisible(imageNavigatorView); if (isImageNavigatorVisible) imageNavigatorAction->setChecked(true); } imageNavigatorAction->setToolTip("Toggle image navigator for navigating through image"); } viewNavigatorAction = new QAction(berry::QtStyleManager::ThemeIcon(QStringLiteral(":/org.mitk.gui.qt.ext/view-manager.svg")),"&View Navigator", nullptr); viewNavigatorFound = window->GetWorkbench()->GetViewRegistry()->Find("org.mitk.views.viewnavigator"); if (viewNavigatorFound) { QObject::connect(viewNavigatorAction, SIGNAL(triggered(bool)), QmitkExtWorkbenchWindowAdvisorHack::undohack, SLOT(onViewNavigator())); viewNavigatorAction->setCheckable(true); // add part listener for view navigator viewNavigatorPartListener.reset(new PartListenerForViewNavigator(viewNavigatorAction)); window->GetPartService()->AddPartListener(viewNavigatorPartListener.data()); berry::IViewPart::Pointer viewnavigatorview = window->GetActivePage()->FindView("org.mitk.views.viewnavigator"); viewNavigatorAction->setChecked(false); if (viewnavigatorview) { bool isViewNavigatorVisible = window->GetActivePage()->IsPartVisible(viewnavigatorview); if (isViewNavigatorVisible) viewNavigatorAction->setChecked(true); } viewNavigatorAction->setToolTip("Toggle View Navigator"); } mainActionsToolBar->addAction(fileOpenAction); mainActionsToolBar->addAction(fileSaveProjectAction); mainActionsToolBar->addAction(closeProjectAction); mainActionsToolBar->addAction(undoAction); mainActionsToolBar->addAction(redoAction); if(this->GetWindowConfigurer()->GetWindow()->GetWorkbench()->GetEditorRegistry()->FindEditor("org.mitk.editors.dicombrowser")) { mainActionsToolBar->addAction(openDicomEditorAction); } if (this->GetWindowConfigurer()->GetWindow()->GetWorkbench()->GetEditorRegistry()->FindEditor("org.mitk.editors.stdmultiwidget")) { mainActionsToolBar->addAction(openStdMultiWidgetEditorAction); } if (this->GetWindowConfigurer()->GetWindow()->GetWorkbench()->GetEditorRegistry()->FindEditor("org.mitk.editors.mxnmultiwidget")) { mainActionsToolBar->addAction(openMxNMultiWidgetEditorAction); } if (imageNavigatorViewFound) { mainActionsToolBar->addAction(imageNavigatorAction); } if (viewNavigatorFound) { mainActionsToolBar->addAction(viewNavigatorAction); } mainWindow->addToolBar(mainActionsToolBar); // ==== Perspective Toolbar ================================== auto qPerspectiveToolbar = new QToolBar; qPerspectiveToolbar->setObjectName("perspectiveToolBar"); if (showPerspectiveToolbar) { qPerspectiveToolbar->addActions(perspGroup->actions()); mainWindow->addToolBar(qPerspectiveToolbar); } else delete qPerspectiveToolbar; if (showViewToolbar) { auto prefService = berry::WorkbenchPlugin::GetDefault()->GetPreferencesService(); berry::IPreferences::Pointer stylePrefs = prefService->GetSystemPreferences()->Node(berry::QtPreferences::QT_STYLES_NODE); bool showCategoryNames = stylePrefs->GetBool(berry::QtPreferences::QT_SHOW_TOOLBAR_CATEGORY_NAMES, true); // Order view descriptors by category QMultiMap categoryViewDescriptorMap; for (const auto &labelViewDescriptorPair : VDMap) { auto viewDescriptor = labelViewDescriptorPair.second; auto category = !viewDescriptor->GetCategoryPath().isEmpty() ? viewDescriptor->GetCategoryPath().back() : QString(); categoryViewDescriptorMap.insert(category, viewDescriptor); } // Create a separate toolbar for each category for (const auto &category : categoryViewDescriptorMap.uniqueKeys()) { auto viewDescriptorsInCurrentCategory = categoryViewDescriptorMap.values(category); if (!viewDescriptorsInCurrentCategory.isEmpty()) { auto toolbar = new QToolBar; toolbar->setObjectName(category + " View Toolbar"); mainWindow->addToolBar(toolbar); if (showCategoryNames && !category.isEmpty()) { auto categoryButton = new QToolButton; categoryButton->setToolButtonStyle(Qt::ToolButtonTextOnly); categoryButton->setText(category); categoryButton->setStyleSheet("background: transparent; margin: 0; padding: 0;"); toolbar->addWidget(categoryButton); connect(categoryButton, &QToolButton::clicked, [toolbar]() { for (QWidget* widget : toolbar->findChildren()) { if (QStringLiteral("qt_toolbar_ext_button") == widget->objectName() && widget->isVisible()) { QMouseEvent pressEvent(QEvent::MouseButtonPress, QPointF(0.0f, 0.0f), Qt::LeftButton, Qt::LeftButton, Qt::NoModifier); QMouseEvent releaseEvent(QEvent::MouseButtonRelease, QPointF(0.0f, 0.0f), Qt::LeftButton, Qt::LeftButton, Qt::NoModifier); QApplication::sendEvent(widget, &pressEvent); QApplication::sendEvent(widget, &releaseEvent); } } }); } for (const auto &viewDescriptor : qAsConst(viewDescriptorsInCurrentCategory)) { auto viewAction = new berry::QtShowViewAction(window, viewDescriptor); toolbar->addAction(viewAction); } } } } QSettings settings(GetQSettingsFile(), QSettings::IniFormat); mainWindow->restoreState(settings.value("ToolbarPosition").toByteArray()); auto qStatusBar = new QStatusBar(); //creating a QmitkStatusBar for Output on the QStatusBar and connecting it with the MainStatusBar auto statusBar = new QmitkStatusBar(qStatusBar); //disabling the SizeGrip in the lower right corner statusBar->SetSizeGripEnabled(false); auto progBar = new QmitkProgressBar(); qStatusBar->addPermanentWidget(progBar, 0); progBar->hide(); // progBar->AddStepsToDo(2); // progBar->Progress(1); mainWindow->setStatusBar(qStatusBar); if (showMemoryIndicator) { auto memoryIndicator = new QmitkMemoryUsageIndicatorView(); qStatusBar->addPermanentWidget(memoryIndicator, 0); } } void QmitkExtWorkbenchWindowAdvisor::PreWindowOpen() { berry::IWorkbenchWindowConfigurer::Pointer configurer = GetWindowConfigurer(); // show the shortcut bar and progress indicator, which are hidden by // default //configurer->SetShowPerspectiveBar(true); //configurer->SetShowFastViewBars(true); //configurer->SetShowProgressIndicator(true); // // add the drag and drop support for the editor area // configurer.addEditorAreaTransfer(EditorInputTransfer.getInstance()); // configurer.addEditorAreaTransfer(ResourceTransfer.getInstance()); // configurer.addEditorAreaTransfer(FileTransfer.getInstance()); // configurer.addEditorAreaTransfer(MarkerTransfer.getInstance()); // configurer.configureEditorAreaDropListener(new EditorAreaDropAdapter( // configurer.getWindow())); this->HookTitleUpdateListeners(configurer); menuPerspectiveListener.reset(new PerspectiveListenerForMenu(this)); configurer->GetWindow()->AddPerspectiveListener(menuPerspectiveListener.data()); configurer->AddEditorAreaTransfer(QStringList("text/uri-list")); configurer->ConfigureEditorAreaDropListener(dropTargetListener.data()); } void QmitkExtWorkbenchWindowAdvisor::PostWindowOpen() { berry::WorkbenchWindowAdvisor::PostWindowOpen(); // Force Rendering Window Creation on startup. berry::IWorkbenchWindowConfigurer::Pointer configurer = GetWindowConfigurer(); ctkPluginContext* context = QmitkCommonExtPlugin::getContext(); ctkServiceReference serviceRef = context->getServiceReference(); if (serviceRef) { mitk::IDataStorageService *dsService = context->getService(serviceRef); if (dsService) { mitk::IDataStorageReference::Pointer dsRef = dsService->GetDataStorage(); mitk::DataStorageEditorInput::Pointer dsInput(new mitk::DataStorageEditorInput(dsRef)); mitk::WorkbenchUtil::OpenEditor(configurer->GetWindow()->GetActivePage(),dsInput); } } auto introPart = configurer->GetWindow()->GetWorkbench()->GetIntroManager()->GetIntro(); if (introPart.IsNotNull()) { configurer->GetWindow()->GetWorkbench()->GetIntroManager()->ShowIntro(GetWindowConfigurer()->GetWindow(), false); } } void QmitkExtWorkbenchWindowAdvisor::onIntro() { QmitkExtWorkbenchWindowAdvisorHack::undohack->onIntro(); } void QmitkExtWorkbenchWindowAdvisor::onHelp() { QmitkExtWorkbenchWindowAdvisorHack::undohack->onHelp(); } void QmitkExtWorkbenchWindowAdvisor::onHelpOpenHelpPerspective() { QmitkExtWorkbenchWindowAdvisorHack::undohack->onHelpOpenHelpPerspective(); } void QmitkExtWorkbenchWindowAdvisor::onAbout() { QmitkExtWorkbenchWindowAdvisorHack::undohack->onAbout(); } //-------------------------------------------------------------------------------- // Ugly hack from here on. Feel free to delete when command framework // and undo buttons are done. //-------------------------------------------------------------------------------- QmitkExtWorkbenchWindowAdvisorHack::QmitkExtWorkbenchWindowAdvisorHack() : QObject() { } QmitkExtWorkbenchWindowAdvisorHack::~QmitkExtWorkbenchWindowAdvisorHack() { } void QmitkExtWorkbenchWindowAdvisorHack::onUndo() { mitk::UndoModel* model = mitk::UndoController::GetCurrentUndoModel(); if (model) { if (mitk::VerboseLimitedLinearUndo* verboseundo = dynamic_cast( model )) { mitk::VerboseLimitedLinearUndo::StackDescription descriptions = verboseundo->GetUndoDescriptions(); if (descriptions.size() >= 1) { MITK_INFO << "Undo " << descriptions.front().second; } } model->Undo(); } else { MITK_ERROR << "No undo model instantiated"; } } void QmitkExtWorkbenchWindowAdvisorHack::onRedo() { mitk::UndoModel* model = mitk::UndoController::GetCurrentUndoModel(); if (model) { if (mitk::VerboseLimitedLinearUndo* verboseundo = dynamic_cast( model )) { mitk::VerboseLimitedLinearUndo::StackDescription descriptions = verboseundo->GetRedoDescriptions(); if (descriptions.size() >= 1) { MITK_INFO << "Redo " << descriptions.front().second; } } model->Redo(); } else { MITK_ERROR << "No undo model instantiated"; } } // safe calls to the complete chain // berry::PlatformUI::GetWorkbench()->GetActiveWorkbenchWindow()->GetActivePage()->FindView("org.mitk.views.imagenavigator"); // to cover for all possible cases of closed pages etc. static void SafeHandleNavigatorView(QString view_query_name) { berry::IWorkbench* wbench = berry::PlatformUI::GetWorkbench(); if( wbench == nullptr ) return; berry::IWorkbenchWindow::Pointer wbench_window = wbench->GetActiveWorkbenchWindow(); if( wbench_window.IsNull() ) return; berry::IWorkbenchPage::Pointer wbench_page = wbench_window->GetActivePage(); if( wbench_page.IsNull() ) return; auto wbench_view = wbench_page->FindView( view_query_name ); if( wbench_view.IsNotNull() ) { bool isViewVisible = wbench_page->IsPartVisible( wbench_view ); if( isViewVisible ) { wbench_page->HideView( wbench_view ); return; } } wbench_page->ShowView( view_query_name ); } void QmitkExtWorkbenchWindowAdvisorHack::onImageNavigator() { // show/hide ImageNavigatorView SafeHandleNavigatorView("org.mitk.views.imagenavigator"); } void QmitkExtWorkbenchWindowAdvisorHack::onViewNavigator() { // show/hide viewnavigatorView SafeHandleNavigatorView("org.mitk.views.viewnavigator"); } void QmitkExtWorkbenchWindowAdvisorHack::onEditPreferences() { QmitkPreferencesDialog _PreferencesDialog(QApplication::activeWindow()); _PreferencesDialog.exec(); } void QmitkExtWorkbenchWindowAdvisorHack::onQuit() { berry::PlatformUI::GetWorkbench()->Close(); } void QmitkExtWorkbenchWindowAdvisorHack::onResetPerspective() { berry::PlatformUI::GetWorkbench()->GetActiveWorkbenchWindow()->GetActivePage()->ResetPerspective(); } void QmitkExtWorkbenchWindowAdvisorHack::onClosePerspective() { berry::IWorkbenchPage::Pointer page = berry::PlatformUI::GetWorkbench()->GetActiveWorkbenchWindow()->GetActivePage(); page->ClosePerspective(page->GetPerspective(), true, true); } void QmitkExtWorkbenchWindowAdvisorHack::onNewWindow() { berry::PlatformUI::GetWorkbench()->OpenWorkbenchWindow(nullptr); } void QmitkExtWorkbenchWindowAdvisorHack::onIntro() { bool hasIntro = berry::PlatformUI::GetWorkbench()->GetIntroManager()->HasIntro(); if (!hasIntro) { QRegExp reg("(.*)(\\n)*"); QRegExp reg2("(\\n)*(.*)"); QFile file(":/org.mitk.gui.qt.ext/index.html"); file.open(QIODevice::ReadOnly | QIODevice::Text); //text file only for reading QString text = QString(file.readAll()); file.close(); QString title = text; title.replace(reg, ""); title.replace(reg2, ""); std::cout << title.toStdString() << std::endl; QMessageBox::information(nullptr, title, text, "Close"); } else { berry::PlatformUI::GetWorkbench()->GetIntroManager()->ShowIntro( berry::PlatformUI::GetWorkbench()->GetActiveWorkbenchWindow(), false); } } void QmitkExtWorkbenchWindowAdvisorHack::onHelp() { ctkPluginContext* context = QmitkCommonExtPlugin::getContext(); if (context == nullptr) { MITK_WARN << "Plugin context not set, unable to open context help"; return; } // Check if the org.blueberry.ui.qt.help plug-in is installed and started QList > plugins = context->getPlugins(); foreach(QSharedPointer p, plugins) { if (p->getSymbolicName() == "org.blueberry.ui.qt.help") { if (p->getState() != ctkPlugin::ACTIVE) { // try to activate the plug-in explicitly try { p->start(ctkPlugin::START_TRANSIENT); } catch (const ctkPluginException& pe) { MITK_ERROR << "Activating org.blueberry.ui.qt.help failed: " << pe.what(); return; } } } } ctkServiceReference eventAdminRef = context->getServiceReference(); ctkEventAdmin* eventAdmin = nullptr; if (eventAdminRef) { eventAdmin = context->getService(eventAdminRef); } if (eventAdmin == nullptr) { MITK_WARN << "ctkEventAdmin service not found. Unable to open context help"; } else { ctkEvent ev("org/blueberry/ui/help/CONTEXTHELP_REQUESTED"); eventAdmin->postEvent(ev); } } void QmitkExtWorkbenchWindowAdvisorHack::onHelpOpenHelpPerspective() { berry::PlatformUI::GetWorkbench()->ShowPerspective("org.blueberry.perspectives.help", berry::PlatformUI::GetWorkbench()->GetActiveWorkbenchWindow()); } void QmitkExtWorkbenchWindowAdvisorHack::onAbout() { auto aboutDialog = new QmitkAboutDialog(QApplication::activeWindow(),nullptr); aboutDialog->open(); } void QmitkExtWorkbenchWindowAdvisor::HookTitleUpdateListeners(berry::IWorkbenchWindowConfigurer::Pointer configurer) { // hook up the listeners to update the window title titlePartListener.reset(new PartListenerForTitle(this)); titlePerspectiveListener.reset(new PerspectiveListenerForTitle(this)); editorPropertyListener.reset(new berry::PropertyChangeIntAdapter< QmitkExtWorkbenchWindowAdvisor>(this, &QmitkExtWorkbenchWindowAdvisor::PropertyChange)); // configurer.getWindow().addPageListener(new IPageListener() { // public void pageActivated(IWorkbenchPage page) { // updateTitle(false); // } // // public void pageClosed(IWorkbenchPage page) { // updateTitle(false); // } // // public void pageOpened(IWorkbenchPage page) { // // do nothing // } // }); configurer->GetWindow()->AddPerspectiveListener(titlePerspectiveListener.data()); configurer->GetWindow()->GetPartService()->AddPartListener(titlePartListener.data()); } QString QmitkExtWorkbenchWindowAdvisor::ComputeTitle() { berry::IWorkbenchWindowConfigurer::Pointer configurer = GetWindowConfigurer(); berry::IWorkbenchPage::Pointer currentPage = configurer->GetWindow()->GetActivePage(); berry::IEditorPart::Pointer activeEditor; if (currentPage) { activeEditor = lastActiveEditor.Lock(); } QString title; berry::IProduct::Pointer product = berry::Platform::GetProduct(); if (product.IsNotNull()) { title = product->GetName(); } if (title.isEmpty()) { // instead of the product name, we use a custom variable for now title = productName; } if(showMitkVersionInfo) { QString mitkVersionInfo = MITK_REVISION_DESC; if(mitkVersionInfo.isEmpty()) mitkVersionInfo = MITK_VERSION_STRING; title += " " + mitkVersionInfo; } if (showVersionInfo) { // add version informatioin QString versions = QString(" (ITK %1.%2.%3 | VTK %4.%5.%6 | Qt %7)") .arg(ITK_VERSION_MAJOR).arg(ITK_VERSION_MINOR).arg(ITK_VERSION_PATCH) .arg(VTK_MAJOR_VERSION).arg(VTK_MINOR_VERSION).arg(VTK_BUILD_VERSION) .arg(QT_VERSION_STR); title += versions; } if (currentPage) { if (activeEditor) { lastEditorTitle = activeEditor->GetTitleToolTip(); if (!lastEditorTitle.isEmpty()) title = lastEditorTitle + " - " + title; } berry::IPerspectiveDescriptor::Pointer persp = currentPage->GetPerspective(); QString label = ""; if (persp) { label = persp->GetLabel(); } berry::IAdaptable* input = currentPage->GetInput(); if (input && input != wbAdvisor->GetDefaultPageInput()) { label = currentPage->GetLabel(); } if (!label.isEmpty()) { title = label + " - " + title; } } title += " (Not for use in diagnosis or treatment of patients)"; return title; } void QmitkExtWorkbenchWindowAdvisor::RecomputeTitle() { berry::IWorkbenchWindowConfigurer::Pointer configurer = GetWindowConfigurer(); QString oldTitle = configurer->GetTitle(); QString newTitle = ComputeTitle(); if (newTitle != oldTitle) { configurer->SetTitle(newTitle); } } void QmitkExtWorkbenchWindowAdvisor::UpdateTitle(bool editorHidden) { berry::IWorkbenchWindowConfigurer::Pointer configurer = GetWindowConfigurer(); berry::IWorkbenchWindow::Pointer window = configurer->GetWindow(); berry::IEditorPart::Pointer activeEditor; berry::IWorkbenchPage::Pointer currentPage = window->GetActivePage(); berry::IPerspectiveDescriptor::Pointer persp; berry::IAdaptable* input = nullptr; if (currentPage) { activeEditor = currentPage->GetActiveEditor(); persp = currentPage->GetPerspective(); input = currentPage->GetInput(); } if (editorHidden) { activeEditor = nullptr; } // Nothing to do if the editor hasn't changed if (activeEditor == lastActiveEditor.Lock() && currentPage == lastActivePage.Lock() && persp == lastPerspective.Lock() && input == lastInput) { return; } - if (!lastActiveEditor.Expired()) + auto lockedLastActiveEditor = lastActiveEditor.Lock(); + + if (lockedLastActiveEditor.IsNotNull()) { - lastActiveEditor.Lock()->RemovePropertyListener(editorPropertyListener.data()); + lockedLastActiveEditor->RemovePropertyListener(editorPropertyListener.data()); } lastActiveEditor = activeEditor; lastActivePage = currentPage; lastPerspective = persp; lastInput = input; if (activeEditor) { activeEditor->AddPropertyListener(editorPropertyListener.data()); } RecomputeTitle(); } void QmitkExtWorkbenchWindowAdvisor::PropertyChange(const berry::Object::Pointer& /*source*/, int propId) { if (propId == berry::IWorkbenchPartConstants::PROP_TITLE) { - if (!lastActiveEditor.Expired()) + auto lockedLastActiveEditor = lastActiveEditor.Lock(); + + if (lockedLastActiveEditor.IsNotNull()) { - QString newTitle = lastActiveEditor.Lock()->GetPartName(); + QString newTitle = lockedLastActiveEditor->GetPartName(); if (lastEditorTitle != newTitle) { RecomputeTitle(); } } } } void QmitkExtWorkbenchWindowAdvisor::SetPerspectiveExcludeList(const QList& v) { this->perspectiveExcludeList = v; } QList QmitkExtWorkbenchWindowAdvisor::GetPerspectiveExcludeList() { return this->perspectiveExcludeList; } void QmitkExtWorkbenchWindowAdvisor::SetViewExcludeList(const QList& v) { this->viewExcludeList = v; } QList QmitkExtWorkbenchWindowAdvisor::GetViewExcludeList() { return this->viewExcludeList; } void QmitkExtWorkbenchWindowAdvisor::PostWindowClose() { berry::IWorkbenchWindow::Pointer window = this->GetWindowConfigurer()->GetWindow(); QMainWindow* mainWindow = static_cast (window->GetShell()->GetControl()); QSettings settings(GetQSettingsFile(), QSettings::IniFormat); settings.setValue("ToolbarPosition", mainWindow->saveState()); } QString QmitkExtWorkbenchWindowAdvisor::GetQSettingsFile() const { QFileInfo settingsInfo = QmitkCommonExtPlugin::getContext()->getDataFile(QT_SETTINGS_FILENAME); return settingsInfo.canonicalFilePath(); } diff --git a/Plugins/org.mitk.gui.qt.flowapplication/src/internal/QmitkFlowApplicationWorkbenchWindowAdvisor.cpp b/Plugins/org.mitk.gui.qt.flowapplication/src/internal/QmitkFlowApplicationWorkbenchWindowAdvisor.cpp index 710e4ebd81..e13edad78c 100644 --- a/Plugins/org.mitk.gui.qt.flowapplication/src/internal/QmitkFlowApplicationWorkbenchWindowAdvisor.cpp +++ b/Plugins/org.mitk.gui.qt.flowapplication/src/internal/QmitkFlowApplicationWorkbenchWindowAdvisor.cpp @@ -1,1145 +1,1151 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #include "QmitkFlowApplicationWorkbenchWindowAdvisor.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "QmitkExtFileSaveProjectAction.h" #include #include #include #include #include #include #include // UGLYYY #include "QmitkFlowApplicationWorkbenchWindowAdvisorHack.h" #include "QmitkFlowApplicationPlugin.h" #include "mitkUndoController.h" #include "mitkVerboseLimitedLinearUndo.h" #include #include #include #include #include #include QmitkFlowApplicationWorkbenchWindowAdvisorHack* QmitkFlowApplicationWorkbenchWindowAdvisorHack::undohack = new QmitkFlowApplicationWorkbenchWindowAdvisorHack(); QString QmitkFlowApplicationWorkbenchWindowAdvisor::QT_SETTINGS_FILENAME = "QtSettings.ini"; class PartListenerForTitle: public berry::IPartListener { public: PartListenerForTitle(QmitkFlowApplicationWorkbenchWindowAdvisor* wa) : windowAdvisor(wa) { } Events::Types GetPartEventTypes() const override { return Events::ACTIVATED | Events::BROUGHT_TO_TOP | Events::CLOSED | Events::HIDDEN | Events::VISIBLE; } void PartActivated(const berry::IWorkbenchPartReference::Pointer& ref) override { if (ref.Cast ()) { windowAdvisor->UpdateTitle(false); } } void PartBroughtToTop(const berry::IWorkbenchPartReference::Pointer& ref) override { if (ref.Cast ()) { windowAdvisor->UpdateTitle(false); } } void PartClosed(const berry::IWorkbenchPartReference::Pointer& /*ref*/) override { windowAdvisor->UpdateTitle(false); } void PartHidden(const berry::IWorkbenchPartReference::Pointer& ref) override { - if (!windowAdvisor->lastActiveEditor.Expired() && - ref->GetPart(false) == windowAdvisor->lastActiveEditor.Lock()) + auto lockedLastActiveEditor = windowAdvisor->lastActiveEditor.Lock(); + + if (lockedLastActiveEditor.IsNotNull() && ref->GetPart(false) == lockedLastActiveEditor) { windowAdvisor->UpdateTitle(true); } } void PartVisible(const berry::IWorkbenchPartReference::Pointer& ref) override { - if (!windowAdvisor->lastActiveEditor.Expired() && - ref->GetPart(false) == windowAdvisor->lastActiveEditor.Lock()) + auto lockedLastActiveEditor = windowAdvisor->lastActiveEditor.Lock(); + + if (lockedLastActiveEditor.IsNotNull() && ref->GetPart(false) == lockedLastActiveEditor) { windowAdvisor->UpdateTitle(false); } } private: QmitkFlowApplicationWorkbenchWindowAdvisor* windowAdvisor; }; class PartListenerForImageNavigator: public berry::IPartListener { public: PartListenerForImageNavigator(QAction* act) : imageNavigatorAction(act) { } Events::Types GetPartEventTypes() const override { return Events::OPENED | Events::CLOSED | Events::HIDDEN | Events::VISIBLE; } void PartOpened(const berry::IWorkbenchPartReference::Pointer& ref) override { if (ref->GetId()=="org.mitk.views.imagenavigator") { imageNavigatorAction->setChecked(true); } } void PartClosed(const berry::IWorkbenchPartReference::Pointer& ref) override { if (ref->GetId()=="org.mitk.views.imagenavigator") { imageNavigatorAction->setChecked(false); } } void PartVisible(const berry::IWorkbenchPartReference::Pointer& ref) override { if (ref->GetId()=="org.mitk.views.imagenavigator") { imageNavigatorAction->setChecked(true); } } void PartHidden(const berry::IWorkbenchPartReference::Pointer& ref) override { if (ref->GetId()=="org.mitk.views.imagenavigator") { imageNavigatorAction->setChecked(false); } } private: QAction* imageNavigatorAction; }; class PerspectiveListenerForTitle: public berry::IPerspectiveListener { public: PerspectiveListenerForTitle(QmitkFlowApplicationWorkbenchWindowAdvisor* wa) : windowAdvisor(wa) , perspectivesClosed(false) { } Events::Types GetPerspectiveEventTypes() const override { return Events::ACTIVATED | Events::SAVED_AS | Events::DEACTIVATED | Events::CLOSED | Events::OPENED; } void PerspectiveActivated(const berry::IWorkbenchPage::Pointer& /*page*/, const berry::IPerspectiveDescriptor::Pointer& /*perspective*/) override { windowAdvisor->UpdateTitle(false); } void PerspectiveSavedAs(const berry::IWorkbenchPage::Pointer& /*page*/, const berry::IPerspectiveDescriptor::Pointer& /*oldPerspective*/, const berry::IPerspectiveDescriptor::Pointer& /*newPerspective*/) override { windowAdvisor->UpdateTitle(false); } void PerspectiveDeactivated(const berry::IWorkbenchPage::Pointer& /*page*/, const berry::IPerspectiveDescriptor::Pointer& /*perspective*/) override { windowAdvisor->UpdateTitle(false); } void PerspectiveOpened(const berry::IWorkbenchPage::Pointer& /*page*/, const berry::IPerspectiveDescriptor::Pointer& /*perspective*/) override { if (perspectivesClosed) { QListIterator i(windowAdvisor->viewActions); while (i.hasNext()) { i.next()->setEnabled(true); } windowAdvisor->fileSaveProjectAction->setEnabled(true); windowAdvisor->undoAction->setEnabled(true); windowAdvisor->redoAction->setEnabled(true); windowAdvisor->imageNavigatorAction->setEnabled(true); windowAdvisor->resetPerspAction->setEnabled(true); } perspectivesClosed = false; } void PerspectiveClosed(const berry::IWorkbenchPage::Pointer& /*page*/, const berry::IPerspectiveDescriptor::Pointer& /*perspective*/) override { berry::IWorkbenchWindow::Pointer wnd = windowAdvisor->GetWindowConfigurer()->GetWindow(); bool allClosed = true; if (wnd->GetActivePage()) { QList perspectives(wnd->GetActivePage()->GetOpenPerspectives()); allClosed = perspectives.empty(); } if (allClosed) { perspectivesClosed = true; QListIterator i(windowAdvisor->viewActions); while (i.hasNext()) { i.next()->setEnabled(false); } windowAdvisor->fileSaveProjectAction->setEnabled(false); windowAdvisor->undoAction->setEnabled(false); windowAdvisor->redoAction->setEnabled(false); windowAdvisor->imageNavigatorAction->setEnabled(false); windowAdvisor->resetPerspAction->setEnabled(false); } } private: QmitkFlowApplicationWorkbenchWindowAdvisor* windowAdvisor; bool perspectivesClosed; }; class PerspectiveListenerForMenu: public berry::IPerspectiveListener { public: PerspectiveListenerForMenu(QmitkFlowApplicationWorkbenchWindowAdvisor* wa) : windowAdvisor(wa) { } Events::Types GetPerspectiveEventTypes() const override { return Events::ACTIVATED | Events::DEACTIVATED; } void PerspectiveActivated(const berry::IWorkbenchPage::Pointer& /*page*/, const berry::IPerspectiveDescriptor::Pointer& perspective) override { QAction* action = windowAdvisor->mapPerspIdToAction[perspective->GetId()]; if (action) { action->setChecked(true); } } void PerspectiveDeactivated(const berry::IWorkbenchPage::Pointer& /*page*/, const berry::IPerspectiveDescriptor::Pointer& perspective) override { QAction* action = windowAdvisor->mapPerspIdToAction[perspective->GetId()]; if (action) { action->setChecked(false); } } private: QmitkFlowApplicationWorkbenchWindowAdvisor* windowAdvisor; }; QmitkFlowApplicationWorkbenchWindowAdvisor::QmitkFlowApplicationWorkbenchWindowAdvisor(berry::WorkbenchAdvisor* wbAdvisor, berry::IWorkbenchWindowConfigurer::Pointer configurer) : berry::WorkbenchWindowAdvisor(configurer) , lastInput(nullptr) , wbAdvisor(wbAdvisor) , showViewToolbar(true) , showVersionInfo(true) , showMitkVersionInfo(true) , showMemoryIndicator(true) , dropTargetListener(new QmitkDefaultDropTargetListener) { productName = QCoreApplication::applicationName(); viewExcludeList.push_back("org.mitk.views.viewnavigator"); } QmitkFlowApplicationWorkbenchWindowAdvisor::~QmitkFlowApplicationWorkbenchWindowAdvisor() { } QWidget* QmitkFlowApplicationWorkbenchWindowAdvisor::CreateEmptyWindowContents(QWidget* parent) { QWidget* parentWidget = static_cast(parent); auto label = new QLabel(parentWidget); label->setText("No perspectives are open. Open a perspective in the Window->Open Perspective menu."); label->setContentsMargins(10,10,10,10); label->setAlignment(Qt::AlignTop); label->setEnabled(false); parentWidget->layout()->addWidget(label); return label; } void QmitkFlowApplicationWorkbenchWindowAdvisor::ShowMemoryIndicator(bool show) { showMemoryIndicator = show; } bool QmitkFlowApplicationWorkbenchWindowAdvisor::GetShowMemoryIndicator() { return showMemoryIndicator; } void QmitkFlowApplicationWorkbenchWindowAdvisor::ShowViewToolbar(bool show) { showViewToolbar = show; } void QmitkFlowApplicationWorkbenchWindowAdvisor::ShowVersionInfo(bool show) { showVersionInfo = show; } void QmitkFlowApplicationWorkbenchWindowAdvisor::ShowMitkVersionInfo(bool show) { showMitkVersionInfo = show; } void QmitkFlowApplicationWorkbenchWindowAdvisor::SetProductName(const QString& product) { productName = product; } void QmitkFlowApplicationWorkbenchWindowAdvisor::SetWindowIcon(const QString& wndIcon) { windowIcon = wndIcon; } void QmitkFlowApplicationWorkbenchWindowAdvisor::PostWindowCreate() { // very bad hack... berry::IWorkbenchWindow::Pointer window = this->GetWindowConfigurer()->GetWindow(); QMainWindow* mainWindow = qobject_cast (window->GetShell()->GetControl()); if (!windowIcon.isEmpty()) { mainWindow->setWindowIcon(QIcon(windowIcon)); } mainWindow->setContextMenuPolicy(Qt::PreventContextMenu); // Load icon theme QIcon::setThemeSearchPaths(QStringList() << QStringLiteral(":/org_mitk_icons/icons/")); QIcon::setThemeName(QStringLiteral("awesome")); // ==== Application menu ============================ QMenuBar* menuBar = mainWindow->menuBar(); menuBar->setContextMenuPolicy(Qt::PreventContextMenu); #ifdef __APPLE__ menuBar->setNativeMenuBar(true); #else menuBar->setNativeMenuBar(false); #endif auto basePath = QStringLiteral(":/org_mitk_icons/icons/awesome/scalable/actions/"); fileSaveProjectAction = new QmitkExtFileSaveProjectAction(window); fileSaveProjectAction->setIcon(berry::QtStyleManager::ThemeIcon(basePath + "document-save.svg")); auto perspGroup = new QActionGroup(menuBar); std::map VDMap; // sort elements (converting vector to map...) QList::const_iterator iter; berry::IViewRegistry* viewRegistry = berry::PlatformUI::GetWorkbench()->GetViewRegistry(); const QList viewDescriptors = viewRegistry->GetViews(); bool skip = false; for (iter = viewDescriptors.begin(); iter != viewDescriptors.end(); ++iter) { // if viewExcludeList is set, it contains the id-strings of view, which // should not appear as an menu-entry in the menu if (viewExcludeList.size() > 0) { for (int i=0; iGetId()) { skip = true; break; } } if (skip) { skip = false; continue; } } if ((*iter)->GetId() == "org.blueberry.ui.internal.introview") continue; if ((*iter)->GetId() == "org.mitk.views.imagenavigator") continue; if ((*iter)->GetId() == "org.mitk.views.viewnavigator") continue; std::pair p((*iter)->GetLabel(), (*iter)); VDMap.insert(p); } std::map::const_iterator MapIter; for (MapIter = VDMap.begin(); MapIter != VDMap.end(); ++MapIter) { berry::QtShowViewAction* viewAction = new berry::QtShowViewAction(window, (*MapIter).second); viewActions.push_back(viewAction); } QMenu* fileMenu = menuBar->addMenu("&File"); fileMenu->setObjectName("FileMenu"); fileMenu->addAction(fileSaveProjectAction); fileMenu->addSeparator(); QAction* fileExitAction = new QmitkFileExitAction(window); fileExitAction->setIcon(berry::QtStyleManager::ThemeIcon(basePath + "system-log-out.svg")); fileExitAction->setShortcut(QKeySequence::Quit); fileExitAction->setObjectName("QmitkFileExitAction"); fileMenu->addAction(fileExitAction); // another bad hack to get an edit/undo menu... QMenu* editMenu = menuBar->addMenu("&Edit"); undoAction = editMenu->addAction(berry::QtStyleManager::ThemeIcon(basePath + "edit-undo.svg"), "&Undo", QmitkFlowApplicationWorkbenchWindowAdvisorHack::undohack, SLOT(onUndo()), QKeySequence("CTRL+Z")); undoAction->setToolTip("Undo the last action (not supported by all modules)"); redoAction = editMenu->addAction(berry::QtStyleManager::ThemeIcon(basePath + "edit-redo.svg"), "&Redo", QmitkFlowApplicationWorkbenchWindowAdvisorHack::undohack, SLOT(onRedo()), QKeySequence("CTRL+Y")); redoAction->setToolTip("execute the last action that was undone again (not supported by all modules)"); // ==== Window Menu ========================== QMenu* windowMenu = menuBar->addMenu("Window"); QMenu* perspMenu = windowMenu->addMenu("&Open Perspective"); windowMenu->addSeparator(); resetPerspAction = windowMenu->addAction("&Reset Perspective", QmitkFlowApplicationWorkbenchWindowAdvisorHack::undohack, SLOT(onResetPerspective())); windowMenu->addSeparator(); windowMenu->addAction("&Preferences...", QmitkFlowApplicationWorkbenchWindowAdvisorHack::undohack, SLOT(onEditPreferences()), QKeySequence("CTRL+P")); // fill perspective menu berry::IPerspectiveRegistry* perspRegistry = window->GetWorkbench()->GetPerspectiveRegistry(); QList perspectives( perspRegistry->GetPerspectives()); skip = false; for (QList::iterator perspIt = perspectives.begin(); perspIt != perspectives.end(); ++perspIt) { // if perspectiveExcludeList is set, it contains the id-strings of perspectives, which // should not appear as an menu-entry in the perspective menu if (perspectiveExcludeList.size() > 0) { for (int i=0; iGetId()) { skip = true; break; } } if (skip) { skip = false; continue; } } QAction* perspAction = new berry::QtOpenPerspectiveAction(window, *perspIt, perspGroup); mapPerspIdToAction.insert((*perspIt)->GetId(), perspAction); } perspMenu->addActions(perspGroup->actions()); // ===== Help menu ==================================== QMenu* helpMenu = menuBar->addMenu("&Help"); helpMenu->addAction("&Welcome",this, SLOT(onIntro())); helpMenu->addAction("&Open Help Perspective", this, SLOT(onHelpOpenHelpPerspective())); helpMenu->addAction("&Context Help",this, SLOT(onHelp()), QKeySequence("F1")); helpMenu->addAction("&About",this, SLOT(onAbout())); // ===================================================== // toolbar for showing file open, undo, redo and other main actions auto mainActionsToolBar = new QToolBar; mainActionsToolBar->setObjectName("mainActionsToolBar"); mainActionsToolBar->setContextMenuPolicy(Qt::PreventContextMenu); #ifdef __APPLE__ mainActionsToolBar->setToolButtonStyle ( Qt::ToolButtonTextUnderIcon ); #else mainActionsToolBar->setToolButtonStyle ( Qt::ToolButtonTextBesideIcon ); #endif basePath = QStringLiteral(":/org.mitk.gui.qt.ext/"); imageNavigatorAction = new QAction(berry::QtStyleManager::ThemeIcon(basePath + "image_navigator.svg"), "&Image Navigator", nullptr); bool imageNavigatorViewFound = window->GetWorkbench()->GetViewRegistry()->Find("org.mitk.views.imagenavigator"); if (imageNavigatorViewFound) { QObject::connect(imageNavigatorAction, SIGNAL(triggered(bool)), QmitkFlowApplicationWorkbenchWindowAdvisorHack::undohack, SLOT(onImageNavigator())); imageNavigatorAction->setCheckable(true); // add part listener for image navigator imageNavigatorPartListener.reset(new PartListenerForImageNavigator(imageNavigatorAction)); window->GetPartService()->AddPartListener(imageNavigatorPartListener.data()); berry::IViewPart::Pointer imageNavigatorView = window->GetActivePage()->FindView("org.mitk.views.imagenavigator"); imageNavigatorAction->setChecked(false); if (imageNavigatorView) { bool isImageNavigatorVisible = window->GetActivePage()->IsPartVisible(imageNavigatorView); if (isImageNavigatorVisible) imageNavigatorAction->setChecked(true); } imageNavigatorAction->setToolTip("Toggle image navigator for navigating through image"); } mainActionsToolBar->addAction(undoAction); mainActionsToolBar->addAction(redoAction); if (imageNavigatorViewFound) { mainActionsToolBar->addAction(imageNavigatorAction); } mainWindow->addToolBar(mainActionsToolBar); // ==== View Toolbar ================================== if (showViewToolbar) { auto prefService = berry::WorkbenchPlugin::GetDefault()->GetPreferencesService(); berry::IPreferences::Pointer stylePrefs = prefService->GetSystemPreferences()->Node(berry::QtPreferences::QT_STYLES_NODE); bool showCategoryNames = stylePrefs->GetBool(berry::QtPreferences::QT_SHOW_TOOLBAR_CATEGORY_NAMES, true); // Order view descriptors by category QMultiMap categoryViewDescriptorMap; for (auto labelViewDescriptorPair : VDMap) { auto viewDescriptor = labelViewDescriptorPair.second; auto category = !viewDescriptor->GetCategoryPath().isEmpty() ? viewDescriptor->GetCategoryPath().back() : QString(); categoryViewDescriptorMap.insert(category, viewDescriptor); } // Create a separate toolbar for each category for (auto category : categoryViewDescriptorMap.uniqueKeys()) { auto viewDescriptorsInCurrentCategory = categoryViewDescriptorMap.values(category); QList > relevantViewDescriptors; for (auto viewDescriptor : viewDescriptorsInCurrentCategory) { if (viewDescriptor->GetId() != "org.mitk.views.flow.control") { relevantViewDescriptors.push_back(viewDescriptor); } } if (!relevantViewDescriptors.isEmpty()) { auto toolbar = new QToolBar; toolbar->setObjectName(category + " View Toolbar"); mainWindow->addToolBar(toolbar); if (showCategoryNames && !category.isEmpty()) { auto categoryButton = new QToolButton; categoryButton->setToolButtonStyle(Qt::ToolButtonTextOnly); categoryButton->setText(category); categoryButton->setStyleSheet("background: transparent; margin: 0; padding: 0;"); toolbar->addWidget(categoryButton); connect(categoryButton, &QToolButton::clicked, [toolbar]() { for (QWidget* widget : toolbar->findChildren()) { if (QStringLiteral("qt_toolbar_ext_button") == widget->objectName() && widget->isVisible()) { QMouseEvent pressEvent(QEvent::MouseButtonPress, QPointF(0.0f, 0.0f), Qt::LeftButton, Qt::LeftButton, Qt::NoModifier); QMouseEvent releaseEvent(QEvent::MouseButtonRelease, QPointF(0.0f, 0.0f), Qt::LeftButton, Qt::LeftButton, Qt::NoModifier); QApplication::sendEvent(widget, &pressEvent); QApplication::sendEvent(widget, &releaseEvent); } } }); } for (auto viewDescriptor : relevantViewDescriptors) { auto viewAction = new berry::QtShowViewAction(window, viewDescriptor); toolbar->addAction(viewAction); } } } } QSettings settings(GetQSettingsFile(), QSettings::IniFormat); mainWindow->restoreState(settings.value("ToolbarPosition").toByteArray()); auto qStatusBar = new QStatusBar(); //creating a QmitkStatusBar for Output on the QStatusBar and connecting it with the MainStatusBar auto statusBar = new QmitkStatusBar(qStatusBar); //disabling the SizeGrip in the lower right corner statusBar->SetSizeGripEnabled(false); auto progBar = new QmitkProgressBar(); qStatusBar->addPermanentWidget(progBar, 0); progBar->hide(); mainWindow->setStatusBar(qStatusBar); if (showMemoryIndicator) { auto memoryIndicator = new QmitkMemoryUsageIndicatorView(); qStatusBar->addPermanentWidget(memoryIndicator, 0); } } void QmitkFlowApplicationWorkbenchWindowAdvisor::PreWindowOpen() { berry::IWorkbenchWindowConfigurer::Pointer configurer = GetWindowConfigurer(); this->HookTitleUpdateListeners(configurer); menuPerspectiveListener.reset(new PerspectiveListenerForMenu(this)); configurer->GetWindow()->AddPerspectiveListener(menuPerspectiveListener.data()); configurer->AddEditorAreaTransfer(QStringList("text/uri-list")); configurer->ConfigureEditorAreaDropListener(dropTargetListener.data()); } void QmitkFlowApplicationWorkbenchWindowAdvisor::PostWindowOpen() { berry::WorkbenchWindowAdvisor::PostWindowOpen(); // Force Rendering Window Creation on startup. berry::IWorkbenchWindowConfigurer::Pointer configurer = GetWindowConfigurer(); ctkPluginContext* context = QmitkFlowApplicationPlugin::GetDefault()->GetPluginContext(); ctkServiceReference serviceRef = context->getServiceReference(); if (serviceRef) { mitk::IDataStorageService *dsService = context->getService(serviceRef); if (dsService) { mitk::IDataStorageReference::Pointer dsRef = dsService->GetDataStorage(); mitk::DataStorageEditorInput::Pointer dsInput(new mitk::DataStorageEditorInput(dsRef)); mitk::WorkbenchUtil::OpenEditor(configurer->GetWindow()->GetActivePage(),dsInput); } } } void QmitkFlowApplicationWorkbenchWindowAdvisor::onIntro() { QmitkFlowApplicationWorkbenchWindowAdvisorHack::undohack->onIntro(); } void QmitkFlowApplicationWorkbenchWindowAdvisor::onHelp() { QmitkFlowApplicationWorkbenchWindowAdvisorHack::undohack->onHelp(); } void QmitkFlowApplicationWorkbenchWindowAdvisor::onHelpOpenHelpPerspective() { QmitkFlowApplicationWorkbenchWindowAdvisorHack::undohack->onHelpOpenHelpPerspective(); } void QmitkFlowApplicationWorkbenchWindowAdvisor::onAbout() { QmitkFlowApplicationWorkbenchWindowAdvisorHack::undohack->onAbout(); } void QmitkFlowApplicationWorkbenchWindowAdvisor::HookTitleUpdateListeners(berry::IWorkbenchWindowConfigurer::Pointer configurer) { // hook up the listeners to update the window title titlePartListener.reset(new PartListenerForTitle(this)); titlePerspectiveListener.reset(new PerspectiveListenerForTitle(this)); editorPropertyListener.reset(new berry::PropertyChangeIntAdapter< QmitkFlowApplicationWorkbenchWindowAdvisor>(this, &QmitkFlowApplicationWorkbenchWindowAdvisor::PropertyChange)); configurer->GetWindow()->AddPerspectiveListener(titlePerspectiveListener.data()); configurer->GetWindow()->GetPartService()->AddPartListener(titlePartListener.data()); } QString QmitkFlowApplicationWorkbenchWindowAdvisor::ComputeTitle() { berry::IWorkbenchWindowConfigurer::Pointer configurer = GetWindowConfigurer(); berry::IWorkbenchPage::Pointer currentPage = configurer->GetWindow()->GetActivePage(); berry::IEditorPart::Pointer activeEditor; if (currentPage) { activeEditor = lastActiveEditor.Lock(); } QString title; berry::IProduct::Pointer product = berry::Platform::GetProduct(); if (product.IsNotNull()) { title = product->GetName(); } if (title.isEmpty()) { // instead of the product name, we use a custom variable for now title = productName; } if(showMitkVersionInfo) { QString mitkVersionInfo = MITK_REVISION_DESC; if(mitkVersionInfo.isEmpty()) mitkVersionInfo = MITK_VERSION_STRING; title += " " + mitkVersionInfo; } if (showVersionInfo) { // add version informatioin QString versions = QString(" (ITK %1.%2.%3 | VTK %4.%5.%6 | Qt %7)") .arg(ITK_VERSION_MAJOR).arg(ITK_VERSION_MINOR).arg(ITK_VERSION_PATCH) .arg(VTK_MAJOR_VERSION).arg(VTK_MINOR_VERSION).arg(VTK_BUILD_VERSION) .arg(QT_VERSION_STR); title += versions; } if (currentPage) { if (activeEditor) { lastEditorTitle = activeEditor->GetTitleToolTip(); if (!lastEditorTitle.isEmpty()) title = lastEditorTitle + " - " + title; } berry::IPerspectiveDescriptor::Pointer persp = currentPage->GetPerspective(); QString label = ""; if (persp) { label = persp->GetLabel(); } berry::IAdaptable* input = currentPage->GetInput(); if (input && input != wbAdvisor->GetDefaultPageInput()) { label = currentPage->GetLabel(); } if (!label.isEmpty()) { title = label + " - " + title; } } title += " (Not for use in diagnosis or treatment of patients)"; return title; } void QmitkFlowApplicationWorkbenchWindowAdvisor::RecomputeTitle() { berry::IWorkbenchWindowConfigurer::Pointer configurer = GetWindowConfigurer(); QString oldTitle = configurer->GetTitle(); QString newTitle = ComputeTitle(); if (newTitle != oldTitle) { configurer->SetTitle(newTitle); } } void QmitkFlowApplicationWorkbenchWindowAdvisor::UpdateTitle(bool editorHidden) { berry::IWorkbenchWindowConfigurer::Pointer configurer = GetWindowConfigurer(); berry::IWorkbenchWindow::Pointer window = configurer->GetWindow(); berry::IEditorPart::Pointer activeEditor; berry::IWorkbenchPage::Pointer currentPage = window->GetActivePage(); berry::IPerspectiveDescriptor::Pointer persp; berry::IAdaptable* input = nullptr; if (currentPage) { activeEditor = currentPage->GetActiveEditor(); persp = currentPage->GetPerspective(); input = currentPage->GetInput(); } if (editorHidden) { activeEditor = nullptr; } // Nothing to do if the editor hasn't changed if (activeEditor == lastActiveEditor.Lock() && currentPage == lastActivePage.Lock() && persp == lastPerspective.Lock() && input == lastInput) { return; } - if (!lastActiveEditor.Expired()) + auto lockedLastActiveEditor = lastActiveEditor.Lock(); + + if (lockedLastActiveEditor.IsNotNull()) { - lastActiveEditor.Lock()->RemovePropertyListener(editorPropertyListener.data()); + lockedLastActiveEditor->RemovePropertyListener(editorPropertyListener.data()); } lastActiveEditor = activeEditor; lastActivePage = currentPage; lastPerspective = persp; lastInput = input; if (activeEditor) { activeEditor->AddPropertyListener(editorPropertyListener.data()); } RecomputeTitle(); } void QmitkFlowApplicationWorkbenchWindowAdvisor::PropertyChange(const berry::Object::Pointer& /*source*/, int propId) { if (propId == berry::IWorkbenchPartConstants::PROP_TITLE) { - if (!lastActiveEditor.Expired()) + auto lockedLastActiveEditor = lastActiveEditor.Lock(); + + if (lockedLastActiveEditor.IsNotNull()) { - QString newTitle = lastActiveEditor.Lock()->GetPartName(); + QString newTitle = lockedLastActiveEditor->GetPartName(); if (lastEditorTitle != newTitle) { RecomputeTitle(); } } } } void QmitkFlowApplicationWorkbenchWindowAdvisor::SetPerspectiveExcludeList(const QList& v) { this->perspectiveExcludeList = v; } QList QmitkFlowApplicationWorkbenchWindowAdvisor::GetPerspectiveExcludeList() { return this->perspectiveExcludeList; } void QmitkFlowApplicationWorkbenchWindowAdvisor::SetViewExcludeList(const QList& v) { this->viewExcludeList = v; } QList QmitkFlowApplicationWorkbenchWindowAdvisor::GetViewExcludeList() { return this->viewExcludeList; } void QmitkFlowApplicationWorkbenchWindowAdvisor::PostWindowClose() { berry::IWorkbenchWindow::Pointer window = this->GetWindowConfigurer()->GetWindow(); QMainWindow* mainWindow = static_cast (window->GetShell()->GetControl()); QSettings settings(GetQSettingsFile(), QSettings::IniFormat); settings.setValue("ToolbarPosition", mainWindow->saveState()); } QString QmitkFlowApplicationWorkbenchWindowAdvisor::GetQSettingsFile() const { QFileInfo settingsInfo = QmitkFlowApplicationPlugin::GetDefault()->GetPluginContext()->getDataFile(QT_SETTINGS_FILENAME); return settingsInfo.canonicalFilePath(); } //-------------------------------------------------------------------------------- // Ugly hack from here on. Feel free to delete when command framework // and undo buttons are done. //-------------------------------------------------------------------------------- QmitkFlowApplicationWorkbenchWindowAdvisorHack::QmitkFlowApplicationWorkbenchWindowAdvisorHack() : QObject() { } QmitkFlowApplicationWorkbenchWindowAdvisorHack::~QmitkFlowApplicationWorkbenchWindowAdvisorHack() { } void QmitkFlowApplicationWorkbenchWindowAdvisorHack::onUndo() { mitk::UndoModel* model = mitk::UndoController::GetCurrentUndoModel(); if (model) { if (mitk::VerboseLimitedLinearUndo* verboseundo = dynamic_cast(model)) { mitk::VerboseLimitedLinearUndo::StackDescription descriptions = verboseundo->GetUndoDescriptions(); if (descriptions.size() >= 1) { MITK_INFO << "Undo " << descriptions.front().second; } } model->Undo(); } else { MITK_ERROR << "No undo model instantiated"; } } void QmitkFlowApplicationWorkbenchWindowAdvisorHack::onRedo() { mitk::UndoModel* model = mitk::UndoController::GetCurrentUndoModel(); if (model) { if (mitk::VerboseLimitedLinearUndo* verboseundo = dynamic_cast(model)) { mitk::VerboseLimitedLinearUndo::StackDescription descriptions = verboseundo->GetRedoDescriptions(); if (descriptions.size() >= 1) { MITK_INFO << "Redo " << descriptions.front().second; } } model->Redo(); } else { MITK_ERROR << "No undo model instantiated"; } } // safe calls to the complete chain // berry::PlatformUI::GetWorkbench()->GetActiveWorkbenchWindow()->GetActivePage()->FindView("org.mitk.views.imagenavigator"); // to cover for all possible cases of closed pages etc. static void SafeHandleNavigatorView(QString view_query_name) { berry::IWorkbench* wbench = berry::PlatformUI::GetWorkbench(); if (wbench == nullptr) return; berry::IWorkbenchWindow::Pointer wbench_window = wbench->GetActiveWorkbenchWindow(); if (wbench_window.IsNull()) return; berry::IWorkbenchPage::Pointer wbench_page = wbench_window->GetActivePage(); if (wbench_page.IsNull()) return; auto wbench_view = wbench_page->FindView(view_query_name); if (wbench_view.IsNotNull()) { bool isViewVisible = wbench_page->IsPartVisible(wbench_view); if (isViewVisible) { wbench_page->HideView(wbench_view); return; } } wbench_page->ShowView(view_query_name); } void QmitkFlowApplicationWorkbenchWindowAdvisorHack::onImageNavigator() { // show/hide ImageNavigatorView SafeHandleNavigatorView("org.mitk.views.imagenavigator"); } void QmitkFlowApplicationWorkbenchWindowAdvisorHack::onEditPreferences() { QmitkPreferencesDialog _PreferencesDialog(QApplication::activeWindow()); _PreferencesDialog.exec(); } void QmitkFlowApplicationWorkbenchWindowAdvisorHack::onQuit() { berry::PlatformUI::GetWorkbench()->Close(); } void QmitkFlowApplicationWorkbenchWindowAdvisorHack::onResetPerspective() { berry::PlatformUI::GetWorkbench()->GetActiveWorkbenchWindow()->GetActivePage()->ResetPerspective(); } void QmitkFlowApplicationWorkbenchWindowAdvisorHack::onClosePerspective() { berry::IWorkbenchPage::Pointer page = berry::PlatformUI::GetWorkbench()->GetActiveWorkbenchWindow()->GetActivePage(); page->ClosePerspective(page->GetPerspective(), true, true); } void QmitkFlowApplicationWorkbenchWindowAdvisorHack::onIntro() { bool hasIntro = berry::PlatformUI::GetWorkbench()->GetIntroManager()->HasIntro(); if (!hasIntro) { QRegExp reg("(.*)(\\n)*"); QRegExp reg2("(\\n)*(.*)"); QFile file(":/org.mitk.gui.qt.ext/index.html"); file.open(QIODevice::ReadOnly | QIODevice::Text); //text file only for reading QString text = QString(file.readAll()); file.close(); QString title = text; title.replace(reg, ""); title.replace(reg2, ""); std::cout << title.toStdString() << std::endl; QMessageBox::information(nullptr, title, text, "Close"); } else { berry::PlatformUI::GetWorkbench()->GetIntroManager()->ShowIntro( berry::PlatformUI::GetWorkbench()->GetActiveWorkbenchWindow(), false); } } void QmitkFlowApplicationWorkbenchWindowAdvisorHack::onHelp() { ctkPluginContext* context = QmitkFlowApplicationPlugin::GetDefault()->GetPluginContext(); if (context == nullptr) { MITK_WARN << "Plugin context not set, unable to open context help"; return; } // Check if the org.blueberry.ui.qt.help plug-in is installed and started QList > plugins = context->getPlugins(); foreach(QSharedPointer p, plugins) { if (p->getSymbolicName() == "org.blueberry.ui.qt.help") { if (p->getState() != ctkPlugin::ACTIVE) { // try to activate the plug-in explicitly try { p->start(ctkPlugin::START_TRANSIENT); } catch (const ctkPluginException& pe) { MITK_ERROR << "Activating org.blueberry.ui.qt.help failed: " << pe.what(); return; } } } } ctkServiceReference eventAdminRef = context->getServiceReference(); ctkEventAdmin* eventAdmin = nullptr; if (eventAdminRef) { eventAdmin = context->getService(eventAdminRef); } if (eventAdmin == nullptr) { MITK_WARN << "ctkEventAdmin service not found. Unable to open context help"; } else { ctkEvent ev("org/blueberry/ui/help/CONTEXTHELP_REQUESTED"); eventAdmin->postEvent(ev); } } void QmitkFlowApplicationWorkbenchWindowAdvisorHack::onHelpOpenHelpPerspective() { berry::PlatformUI::GetWorkbench()->ShowPerspective("org.blueberry.perspectives.help", berry::PlatformUI::GetWorkbench()->GetActiveWorkbenchWindow()); } void QmitkFlowApplicationWorkbenchWindowAdvisorHack::onAbout() { auto aboutDialog = new QmitkAboutDialog(QApplication::activeWindow(), nullptr); aboutDialog->open(); } diff --git a/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkDataNodeAddToSemanticRelationsAction.cpp b/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkDataNodeAddToSemanticRelationsAction.cpp index 536ddcc254..e587cb8cc3 100644 --- a/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkDataNodeAddToSemanticRelationsAction.cpp +++ b/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkDataNodeAddToSemanticRelationsAction.cpp @@ -1,247 +1,249 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ // semantic relations plugin #include "QmitkDataNodeAddToSemanticRelationsAction.h" #include "QmitkDataNodeRemoveFromSemanticRelationsAction.h" // semantic relations module #include #include #include #include #include #include #include #include #include // mitk gui common plugin #include // mitk core #include // qt #include // namespace that contains the concrete action namespace AddToSemanticRelationsAction { void Run(mitk::DataStorage* dataStorage, const mitk::DataNode* dataNode) { if (nullptr == dataStorage || nullptr == dataNode) { return; } if (mitk::NodePredicates::GetImagePredicate()->CheckNode(dataNode)) { try { AddImage(dataStorage, dataNode); } catch (mitk::SemanticRelationException& e) { mitkReThrow(e); } } else if (mitk::NodePredicates::GetSegmentationPredicate()->CheckNode(dataNode)) { try { AddSegmentation(dataStorage, dataNode); } catch (mitk::SemanticRelationException& e) { mitkReThrow(e); } } } void AddImage(mitk::DataStorage* dataStorage, const mitk::DataNode* image) { mitk::SemanticTypes::InformationType informationType; mitk::SemanticTypes::ExaminationPeriod examinationPeriod; mitk::SemanticRelationsDataStorageAccess::DataNodeVector allSpecificImages; try { mitk::SemanticTypes::CaseID caseID = GetCaseIDFromDataNode(image); informationType = GetDICOMModalityFromDataNode(image); // see if the examination period - information type cell is already taken examinationPeriod = FindFittingExaminationPeriod(image); auto semanticRelationsDataStorageAccess = mitk::SemanticRelationsDataStorageAccess(dataStorage); try { allSpecificImages = semanticRelationsDataStorageAccess.GetAllSpecificImages(caseID, informationType, examinationPeriod); } catch (const mitk::SemanticRelationException&) { // just continue since an exception means that there is no specific image } } catch (mitk::SemanticRelationException& e) { mitkReThrow(e); } if (!allSpecificImages.empty()) { // examination period - information type cell is already taken // ask if cell should be overwritten QMessageBox::StandardButton answerButton = QMessageBox::question(nullptr, "Specific image already exists.", QString::fromStdString("Force overwriting existing image " + informationType + " at " + examinationPeriod.name + "?"), QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes); if (answerButton == QMessageBox::Yes) { try { // remove already existent images at specific cell for (const auto& specificImage : allSpecificImages) { RemoveFromSemanticRelationsAction::Run(dataStorage, specificImage); } } catch (mitk::SemanticRelationException& e) { mitkReThrow(e); return; } } else { // else case is: no overwriting return; } } // specific image does not exist or has been removed; adding the image should work mitk::SemanticRelationsIntegration semanticRelationsIntegration; try { semanticRelationsIntegration.AddImage(image); } catch (mitk::SemanticRelationException& e) { mitkReThrow(e); } } void AddSegmentation(mitk::DataStorage* dataStorage, const mitk::DataNode* segmentation) { if (nullptr == segmentation) { return; } mitk::BaseData* baseData = segmentation->GetData(); if (nullptr == baseData) { return; } // continue with valid segmentation data // get parent node of the current segmentation node with the node predicate mitk::DataStorage::SetOfObjects::ConstPointer parentNodes = dataStorage->GetSources(segmentation, mitk::NodePredicates::GetImagePredicate(), false); if (parentNodes->empty()) { // segmentation without corresponding image will not be added QMessageBox msgBox(QMessageBox::Warning, "Could not add the selected segmentation.", "The program wasn't able to correctly add the selected segmentation.\n" "Reason: No parent image found"); msgBox.exec(); return; } // check for already existing, identifying base properties auto caseIDPropertyName = mitk::GetCaseIDDICOMProperty(); auto nodeIDPropertyName = mitk::GetNodeIDDICOMProperty(); mitk::BaseProperty* caseIDProperty = baseData->GetProperty(caseIDPropertyName.c_str()); mitk::BaseProperty* nodeIDProperty = baseData->GetProperty(nodeIDPropertyName.c_str()); 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::SemanticTypes::ID nodeID; try { caseID = mitk::GetCaseIDFromDataNode(parentNodes->front()); nodeID = mitk::GetIDFromDataNode(parentNodes->front()); } catch (mitk::SemanticRelationException& e) { mitkReThrow(e); return; } // transfer DICOM tags to the segmentation node baseData->SetProperty(caseIDPropertyName, mitk::TemporoSpatialStringProperty::New(caseID)); // add UID to distinguish between different segmentations of the same parent node baseData->SetProperty(nodeIDPropertyName, mitk::TemporoSpatialStringProperty::New(nodeID + mitk::UIDGeneratorBoost::GenerateUID())); } // add the parent node if not already existent if (!mitk::SemanticRelationsInference::InstanceExists(parentNodes->front())) { AddImage(dataStorage, parentNodes->front()); } mitk::SemanticRelationsIntegration semanticRelationsIntegration; try { // add the segmentation with its parent image to the semantic relations storage semanticRelationsIntegration.AddSegmentation(segmentation, parentNodes->front()); } catch (mitk::SemanticRelationException& e) { mitkReThrow(e); } } } QmitkDataNodeAddToSemanticRelationsAction::QmitkDataNodeAddToSemanticRelationsAction(QWidget* parent, berry::IWorkbenchPartSite::Pointer workbenchPartSite) : QAction(parent) , QmitkAbstractDataNodeAction(workbenchPartSite) { setText(tr("Add to semantic relations")); InitializeAction(); } QmitkDataNodeAddToSemanticRelationsAction::QmitkDataNodeAddToSemanticRelationsAction(QWidget* parent, berry::IWorkbenchPartSite* workbenchPartSite) : QAction(parent) , QmitkAbstractDataNodeAction(berry::IWorkbenchPartSite::Pointer(workbenchPartSite)) { setText(tr("Add to semantic relations")); InitializeAction(); } void QmitkDataNodeAddToSemanticRelationsAction::InitializeAction() { connect(this, &QAction::triggered, this, &QmitkDataNodeAddToSemanticRelationsAction::OnActionTriggered); } void QmitkDataNodeAddToSemanticRelationsAction::OnActionTriggered(bool /*checked*/) { - if (m_DataStorage.IsExpired()) + auto dataStorage = m_DataStorage.Lock(); + + if (dataStorage.IsNull()) { return; } auto dataNode = GetSelectedNode(); - AddToSemanticRelationsAction::Run(m_DataStorage.Lock(), dataNode); + AddToSemanticRelationsAction::Run(dataStorage, dataNode); } diff --git a/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkDataNodeRemoveFromSemanticRelationsAction.cpp b/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkDataNodeRemoveFromSemanticRelationsAction.cpp index 902647c973..e7b33a31a6 100644 --- a/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkDataNodeRemoveFromSemanticRelationsAction.cpp +++ b/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkDataNodeRemoveFromSemanticRelationsAction.cpp @@ -1,127 +1,129 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ // semantic relations plugin #include "QmitkDataNodeRemoveFromSemanticRelationsAction.h" // semantic relations module #include #include #include // mitk gui common plugin #include // qt #include // namespace that contains the concrete action namespace RemoveFromSemanticRelationsAction { void Run(mitk::DataStorage* dataStorage, const mitk::DataNode* dataNode) { if (nullptr == dataStorage || nullptr == dataNode) { return; } if (mitk::NodePredicates::GetImagePredicate()->CheckNode(dataNode)) { try { RemoveImage(dataStorage, dataNode); } catch (mitk::SemanticRelationException& e) { mitkReThrow(e); } } else if (mitk::NodePredicates::GetSegmentationPredicate()->CheckNode(dataNode)) { try { RemoveSegmentation(dataNode); } catch (mitk::SemanticRelationException& e) { mitkReThrow(e); } } } void RemoveImage(mitk::DataStorage* dataStorage, const mitk::DataNode* image) { mitk::SemanticRelationsIntegration semanticRelationsIntegration; try { // remove each corresponding segmentation from the semantic relations storage mitk::DataStorage::SetOfObjects::ConstPointer childNodes = dataStorage->GetDerivations(image, mitk::NodePredicates::GetSegmentationPredicate(), false); for (auto it = childNodes->Begin(); it != childNodes->End(); ++it) { RemoveSegmentation(it->Value()); } // remove the image from the semantic relations storage semanticRelationsIntegration.RemoveImage(image); } catch (mitk::SemanticRelationException& e) { mitkReThrow(e); } } void RemoveSegmentation(const mitk::DataNode* segmentation) { mitk::SemanticRelationsIntegration semanticRelationsIntegration; try { // remove the segmentation from the semantic relations storage semanticRelationsIntegration.RemoveSegmentation(segmentation); } catch (mitk::SemanticRelationException& e) { mitkReThrow(e); } } } QmitkDataNodeRemoveFromSemanticRelationsAction::QmitkDataNodeRemoveFromSemanticRelationsAction(QWidget* parent, berry::IWorkbenchPartSite::Pointer workbenchPartSite) : QAction(parent) , QmitkAbstractDataNodeAction(workbenchPartSite) { setText(tr("Remove from semantic relations")); InitializeAction(); } QmitkDataNodeRemoveFromSemanticRelationsAction::QmitkDataNodeRemoveFromSemanticRelationsAction(QWidget* parent, berry::IWorkbenchPartSite* workbenchPartSite) : QAction(parent) , QmitkAbstractDataNodeAction(berry::IWorkbenchPartSite::Pointer(workbenchPartSite)) { setText(tr("Remove from semantic relations")); InitializeAction(); } void QmitkDataNodeRemoveFromSemanticRelationsAction::InitializeAction() { connect(this, &QAction::triggered, this, &QmitkDataNodeRemoveFromSemanticRelationsAction::OnActionTriggered); } void QmitkDataNodeRemoveFromSemanticRelationsAction::OnActionTriggered(bool /*checked*/) { - if (m_DataStorage.IsExpired()) + auto dataStorage = m_DataStorage.Lock(); + + if (dataStorage.IsNull()) { return; } auto dataNode = GetSelectedNode(); - RemoveFromSemanticRelationsAction::Run(m_DataStorage.Lock(),dataNode); + RemoveFromSemanticRelationsAction::Run(dataStorage, dataNode); } diff --git a/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkDataNodeSetControlPointAction.cpp b/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkDataNodeSetControlPointAction.cpp index 125503f0da..58a8521275 100644 --- a/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkDataNodeSetControlPointAction.cpp +++ b/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkDataNodeSetControlPointAction.cpp @@ -1,170 +1,170 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ // semantic relations plugin #include "QmitkDataNodeSetControlPointAction.h" #include "QmitkDataNodeRemoveFromSemanticRelationsAction.h" // semantic relations module #include #include #include #include #include #include #include // semantic relations UI module #include "QmitkControlPointDialog.h" // qt #include #include QmitkDataNodeSetControlPointAction::QmitkDataNodeSetControlPointAction(QWidget* parent, berry::IWorkbenchPartSite::Pointer workbenchPartSite) : QAction(parent) , QmitkAbstractDataNodeAction(workbenchPartSite) { setText(tr("Set control point")); m_Parent = parent; InitializeAction(); } QmitkDataNodeSetControlPointAction::QmitkDataNodeSetControlPointAction(QWidget* parent, berry::IWorkbenchPartSite* workbenchPartSite) : QAction(parent) , QmitkAbstractDataNodeAction(berry::IWorkbenchPartSite::Pointer(workbenchPartSite)) { setText(tr("Set control point")); m_Parent = parent; InitializeAction(); } void QmitkDataNodeSetControlPointAction::InitializeAction() { connect(this, &QAction::triggered, this, &QmitkDataNodeSetControlPointAction::OnActionTriggered); } void QmitkDataNodeSetControlPointAction::OnActionTriggered(bool /*checked*/) { - if (m_DataStorage.IsExpired()) + auto dataStorage = m_DataStorage.Lock(); + + if (dataStorage.IsNull()) { return; } - auto dataStorage = m_DataStorage.Lock(); - auto dataNode = GetSelectedNode(); if (dataNode.IsNull()) { return; } QmitkControlPointDialog* inputDialog = new QmitkControlPointDialog(m_Parent); inputDialog->setWindowTitle("Set control point"); inputDialog->SetCurrentDate(mitk::SemanticRelationsInference::GetControlPointOfImage(dataNode)); int dialogReturnValue = inputDialog->exec(); if (QDialog::Rejected == dialogReturnValue) { return; } const QDate& userSelectedDate = inputDialog->GetCurrentDate(); mitk::SemanticTypes::ControlPoint controlPoint; controlPoint.UID = mitk::UIDGeneratorBoost::GenerateUID(); controlPoint.date = boost::gregorian::date(userSelectedDate.year(), userSelectedDate.month(), userSelectedDate.day()); mitk::SemanticTypes::InformationType informationType; mitk::SemanticTypes::ExaminationPeriod examinationPeriod; mitk::SemanticRelationsDataStorageAccess::DataNodeVector allSpecificImages; try { mitk::SemanticTypes::CaseID caseID = mitk::GetCaseIDFromDataNode(dataNode); informationType = mitk::SemanticRelationsInference::GetInformationTypeOfImage(dataNode); // see if the examination period - information type cell is already taken examinationPeriod = mitk::FindFittingExaminationPeriod(caseID, controlPoint); auto semanticRelationsDataStorageAccess = mitk::SemanticRelationsDataStorageAccess(dataStorage); try { allSpecificImages = semanticRelationsDataStorageAccess.GetAllSpecificImages(caseID, informationType, examinationPeriod); } catch (const mitk::SemanticRelationException&) { // just continue since an exception means that there is no specific image } } catch (const mitk::SemanticRelationException& e) { std::stringstream exceptionMessage; exceptionMessage << e; QMessageBox msgBox(QMessageBox::Warning, "Could not set the control point.", "The program wasn't able to correctly set the control point.\n" "Reason:\n" + QString::fromStdString(exceptionMessage.str() + "\n")); msgBox.exec(); } if (!allSpecificImages.empty()) { // examination period - information type cell is already taken // ask if cell should be overwritten QMessageBox::StandardButton answerButton = QMessageBox::question(nullptr, "Specific image already exists.", QString::fromStdString("Force overwriting existing image " + informationType + " at " + examinationPeriod.name + "?"), QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes); if (answerButton == QMessageBox::Yes) { try { // remove already existent images at specific cell for (const auto& specificImage : allSpecificImages) { RemoveFromSemanticRelationsAction::Run(dataStorage, specificImage); } } catch (const mitk::SemanticRelationException& e) { std::stringstream exceptionMessage; exceptionMessage << e; QMessageBox msgBox(QMessageBox::Warning, "Could not set the control point.", "The program wasn't able to correctly set the control point.\n" "Reason:\n" + QString::fromStdString(exceptionMessage.str() + "\n")); msgBox.exec(); } } else { // else case is: no overwriting return; } } // specific image does not exist or has been removed; setting the control point should work mitk::SemanticRelationsIntegration semanticRelationsIntegration; try { semanticRelationsIntegration.UnlinkImageFromControlPoint(dataNode); semanticRelationsIntegration.SetControlPointOfImage(dataNode, controlPoint); } catch (const mitk::SemanticRelationException& e) { std::stringstream exceptionMessage; exceptionMessage << e; QMessageBox msgBox(QMessageBox::Warning, "Could not set the control point.", "The program wasn't able to correctly set the control point.\n" "Reason:\n" + QString::fromStdString(exceptionMessage.str() + "\n")); msgBox.exec(); } } diff --git a/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkDataNodeSetInformationTypeAction.cpp b/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkDataNodeSetInformationTypeAction.cpp index 304758f658..079475afee 100644 --- a/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkDataNodeSetInformationTypeAction.cpp +++ b/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkDataNodeSetInformationTypeAction.cpp @@ -1,161 +1,161 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ // semantic relations plugin #include "QmitkDataNodeSetInformationTypeAction.h" #include "QmitkDataNodeRemoveFromSemanticRelationsAction.h" // semantic relations module #include #include #include #include #include #include // qt #include #include QmitkDataNodeSetInformationTypeAction::QmitkDataNodeSetInformationTypeAction(QWidget* parent, berry::IWorkbenchPartSite::Pointer workbenchPartSite) : QAction(parent) , QmitkAbstractDataNodeAction(workbenchPartSite) { setText(tr("Set information type")); m_Parent = parent; InitializeAction(); } QmitkDataNodeSetInformationTypeAction::QmitkDataNodeSetInformationTypeAction(QWidget* parent, berry::IWorkbenchPartSite* workbenchPartSite) : QAction(parent) , QmitkAbstractDataNodeAction(berry::IWorkbenchPartSite::Pointer(workbenchPartSite)) { setText(tr("Set information type")); m_Parent = parent; InitializeAction(); } void QmitkDataNodeSetInformationTypeAction::InitializeAction() { connect(this, &QAction::triggered, this, &QmitkDataNodeSetInformationTypeAction::OnActionTriggered); } void QmitkDataNodeSetInformationTypeAction::OnActionTriggered(bool /*checked*/) { - if (m_DataStorage.IsExpired()) + auto dataStorage = m_DataStorage.Lock(); + + if (dataStorage.IsNull()) { return; } - auto dataStorage = m_DataStorage.Lock(); - auto dataNode = GetSelectedNode(); if (dataNode.IsNull()) { return; } QInputDialog* inputDialog = new QInputDialog(m_Parent); inputDialog->setWindowTitle(tr("Set information type of selected node")); inputDialog->setLabelText(tr("Information type:")); inputDialog->setTextValue(QString::fromStdString(mitk::SemanticRelationsInference::GetInformationTypeOfImage(dataNode))); inputDialog->setMinimumSize(250, 100); int dialogReturnValue = inputDialog->exec(); if (QDialog::Rejected == dialogReturnValue) { return; } mitk::SemanticTypes::InformationType informationType = inputDialog->textValue().toStdString(); mitk::SemanticTypes::ExaminationPeriod examinationPeriod; mitk::SemanticTypes::ControlPoint controlPoint; mitk::SemanticRelationsDataStorageAccess::DataNodeVector allSpecificImages; try { mitk::SemanticTypes::CaseID caseID = mitk::GetCaseIDFromDataNode(dataNode); controlPoint = mitk::SemanticRelationsInference::GetControlPointOfImage(dataNode); // see if the examination period - information type cell is already taken examinationPeriod = mitk::FindFittingExaminationPeriod(caseID, controlPoint); auto semanticRelationsDataStorageAccess = mitk::SemanticRelationsDataStorageAccess(dataStorage); try { allSpecificImages = semanticRelationsDataStorageAccess.GetAllSpecificImages(caseID, informationType, examinationPeriod); } catch (const mitk::SemanticRelationException&) { // just continue since an exception means that there is no specific image } } catch (const mitk::SemanticRelationException& e) { std::stringstream exceptionMessage; exceptionMessage << e; QMessageBox msgBox(QMessageBox::Warning, "Could not set the information type.", "The program wasn't able to correctly set the information type.\n" "Reason:\n" + QString::fromStdString(exceptionMessage.str() + "\n")); msgBox.exec(); } if (!allSpecificImages.empty()) { // examination period - information type cell is already taken // ask if cell should be overwritten QMessageBox::StandardButton answerButton = QMessageBox::question(nullptr, "Specific image already exists.", QString::fromStdString("Force overwriting existing image " + informationType + " at " + examinationPeriod.name + "?"), QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes); if (answerButton == QMessageBox::Yes) { try { // remove already existent images at specific cell for (const auto& specificImage : allSpecificImages) { RemoveFromSemanticRelationsAction::Run(dataStorage, specificImage); } } catch (const mitk::SemanticRelationException& e) { std::stringstream exceptionMessage; exceptionMessage << e; QMessageBox msgBox(QMessageBox::Warning, "Could not set the information type.", "The program wasn't able to correctly set the information type.\n" "Reason:\n" + QString::fromStdString(exceptionMessage.str() + "\n")); msgBox.exec(); } } else { // else case is: no overwriting return; } } mitk::SemanticRelationsIntegration semanticRelationsIntegration; try { semanticRelationsIntegration.SetInformationType(dataNode, informationType); } catch (const mitk::SemanticRelationException& e) { std::stringstream exceptionMessage; exceptionMessage << e; QMessageBox msgBox(QMessageBox::Warning, "Could not set the information type.", "The program wasn't able to correctly set the information type.\n" "Reason:\n" + QString::fromStdString(exceptionMessage.str() + "\n")); msgBox.exec(); } } diff --git a/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkDataSetOpenInAction.cpp b/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkDataSetOpenInAction.cpp index 4447ba851d..3156db0e06 100644 --- a/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkDataSetOpenInAction.cpp +++ b/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkDataSetOpenInAction.cpp @@ -1,158 +1,160 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ // semantic relations plugin #include "QmitkDataSetOpenInAction.h" // mitk core #include #include // render window manager module #include #include // semantic relations module #include "mitkNodePredicates.h" // mitk gui qt application plugin #include // qt #include #include namespace OpenInAction { void Run(mitk::DataStorage::Pointer dataStorage, mitk::DataNode::Pointer imageNode, mitk::BaseRenderer* renderer /*= nullptr*/) { if (dataStorage.IsNull()) { return; } if (imageNode.IsNull()) { return; } auto renderWindowLayerController = std::make_unique(); renderWindowLayerController->SetDataStorage(dataStorage); // get currently fixed layer nodes of the specified renderer // remove them from the specified renderer auto layerStack = mitk::RenderWindowLayerUtilities::GetLayerStack(dataStorage, renderer, true); for (auto& fixedLayer : layerStack) { // hide all nodes of the specified renderer fixedLayer.second->SetVisibility(false, renderer); fixedLayer.second->Modified(); } // add the selected data node to the specified renderer // only needed in case the render window manager plugin is not used imageNode->SetBoolProperty("fixedLayer", true, renderer); // make is visible imageNode->SetVisibility(true, renderer); imageNode->Modified(); // move node to front, which also request a render update renderWindowLayerController->MoveNodeToFront(imageNode, renderer); QList visibleNodes; visibleNodes.push_back(imageNode); // get all corresponding segmentations of this node mitk::DataStorage::SetOfObjects::ConstPointer segmentationNodes = dataStorage->GetDerivations(imageNode, mitk::NodePredicates::GetSegmentationPredicate(), false); for (auto it = segmentationNodes->Begin(); it != segmentationNodes->End(); ++it) { auto segmentation = it->Value(); segmentation->SetBoolProperty("fixedLayer", true, renderer); segmentation->SetVisibility(true, renderer); // move node to front, which also request a render update renderWindowLayerController->MoveNodeToFront(segmentation, renderer); visibleNodes.push_back(segmentation); } ReinitAction::Run(berry::IWorkbenchPartSite::Pointer(), dataStorage, visibleNodes, renderer); } } QmitkDataSetOpenInAction::QmitkDataSetOpenInAction(QWidget* parent, berry::IWorkbenchPartSite::Pointer workbenchPartSite) : QmitkDataNodeOpenInAction(parent, workbenchPartSite) { setText(tr("Open in")); InitializeAction(); } QmitkDataSetOpenInAction::QmitkDataSetOpenInAction(QWidget* parent, berry::IWorkbenchPartSite* workbenchPartSite) : QmitkDataNodeOpenInAction(parent, berry::IWorkbenchPartSite::Pointer(workbenchPartSite)) { setText(tr("Open in")); InitializeAction(); } void QmitkDataSetOpenInAction::InitializeAction() { setCheckable(true); setMenu(new QMenu); connect(menu(), &QMenu::aboutToShow, this, &QmitkDataSetOpenInAction::OnMenuAboutToShow); SetControlledRenderer(); } void QmitkDataSetOpenInAction::OnMenuAboutToShow() { menu()->clear(); QAction* action; QStringList rendererNames; for (const auto& renderer : m_ControlledRenderer) { rendererNames.append(renderer->GetName()); } rendererNames.sort(); for (const auto& rendererName : rendererNames) { action = menu()->addAction(rendererName); connect(action, &QAction::triggered, this, &QmitkDataSetOpenInAction::OnActionTriggered); } } void QmitkDataSetOpenInAction::OnActionTriggered(bool /*checked*/) { - if (m_DataStorage.IsExpired()) + auto dataStorage = m_DataStorage.Lock(); + + if (dataStorage.IsNull()) { return; } auto dataNode = GetSelectedNode(); if (dataNode.IsNull()) { return; } QAction* senderAction = qobject_cast(QObject::sender()); if (nullptr == senderAction) { return; } std::string selectedRenderer = senderAction->text().toStdString(); mitk::BaseRenderer* renderer = mitk::BaseRenderer::GetByName(selectedRenderer); if (nullptr == renderer) { return; } - OpenInAction::Run(m_DataStorage.Lock(), dataNode, renderer); + OpenInAction::Run(dataStorage, dataNode, renderer); } diff --git a/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkFocusOnLesionAction.cpp b/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkFocusOnLesionAction.cpp index 46d828e71d..d54629da0c 100644 --- a/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkFocusOnLesionAction.cpp +++ b/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkFocusOnLesionAction.cpp @@ -1,122 +1,125 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ // semantic relations plugin #include "QmitkFocusOnLesionAction.h" #include "QmitkLabelSetJumpToAction.h" // mitk core #include #include // render window manager module #include #include // semantic relations module #include #include // qt #include #include QmitkFocusOnLesionAction::QmitkFocusOnLesionAction(QWidget* parent, berry::IWorkbenchPartSite::Pointer workbenchPartSite) : QAction(parent) , QmitkAbstractDataNodeAction(workbenchPartSite) { setText(tr("Focus on lesion")); InitializeAction(); } QmitkFocusOnLesionAction::QmitkFocusOnLesionAction(QWidget* parent, berry::IWorkbenchPartSite* workbenchPartSite) : QAction(parent) , QmitkAbstractDataNodeAction(berry::IWorkbenchPartSite::Pointer(workbenchPartSite)) { setText(tr("Focus on lesion")); InitializeAction(); } void QmitkFocusOnLesionAction::SetSelectedLesion(mitk::SemanticTypes::Lesion selectedLesion) { m_Lesion = selectedLesion; } void QmitkFocusOnLesionAction::InitializeAction() { setCheckable(true); connect(this, &QAction::triggered, this, &QmitkFocusOnLesionAction::OnActionTriggered); SetControlledRenderer(); } void QmitkFocusOnLesionAction::OnActionTriggered(bool /*checked*/) { - if (m_WorkbenchPartSite.Expired()) + auto workbenchPartSite = m_WorkbenchPartSite.Lock(); + + if (workbenchPartSite.IsNull()) { return; } - if (m_DataStorage.IsExpired()) + auto dataStorage = m_DataStorage.Lock(); + + if (dataStorage.IsNull()) { return; } - auto dataStorage = m_DataStorage.Lock(); auto renderWindowLayerController = std::make_unique(); renderWindowLayerController->SetDataStorage(dataStorage); // check each renderer to see if it contains a segmentation represented by the currently selected lesion for (const auto& renderer : m_ControlledRenderer) { // get currently fixed layer nodes of the specified renderer auto layerStack = mitk::RenderWindowLayerUtilities::GetLayerStack(dataStorage, renderer, true); // check each node to see if it is a segmentation represented by the currently selected lesion for (const auto& layer : layerStack) { auto dataNode = layer.second; bool isVisible = false; if (dataNode.IsNotNull()) { dataNode->GetBoolProperty("visible", isVisible, renderer); } // only focus in renderer where the segmentation is visible if (!isVisible) { continue; } auto currentLesion = mitk::SemanticRelationsInference::GetLesionOfSegmentation(dataNode); if (currentLesion.UID == m_Lesion.UID) { // jump to this lesion in the specified renderer - LabelSetJumpToAction::Run(m_WorkbenchPartSite.Lock(), dataNode, renderer); + LabelSetJumpToAction::Run(workbenchPartSite, dataNode, renderer); } } } } void QmitkFocusOnLesionAction::SetControlledRenderer() { const mitk::RenderingManager::RenderWindowVector allRegisteredRenderWindows = mitk::RenderingManager::GetInstance()->GetAllRegisteredRenderWindows(); mitk::BaseRenderer *baseRenderer = nullptr; m_ControlledRenderer.clear(); for (const auto& renderWindow : allRegisteredRenderWindows) { baseRenderer = mitk::BaseRenderer::GetInstance(renderWindow); if (nullptr != baseRenderer) { m_ControlledRenderer.push_back(baseRenderer); } } } diff --git a/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkLabelSetJumpToAction.cpp b/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkLabelSetJumpToAction.cpp index 23cefceab6..3585dfc626 100644 --- a/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkLabelSetJumpToAction.cpp +++ b/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkLabelSetJumpToAction.cpp @@ -1,119 +1,121 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ // mitk gui qt application plugin #include "QmitkLabelSetJumpToAction.h" // mitk core #include #include #include // mitk multi label module #include // qt #include #include namespace LabelSetJumpToAction { void Run(berry::IWorkbenchPartSite::Pointer workbenchPartSite, const mitk::DataNode* dataNode, mitk::BaseRenderer* baseRenderer /*= nullptr*/) { if (workbenchPartSite.IsNull()) { return; } if (nullptr == dataNode) { return; } mitk::LabelSetImage* labelSetImage = dynamic_cast(dataNode->GetData()); if (nullptr == labelSetImage) { return; } unsigned 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 = mitk::WorkbenchUtil::GetRenderWindowPart(workbenchPartSite->GetPage(), mitk::WorkbenchUtil::NONE); if (nullptr == renderWindowPart) { renderWindowPart = mitk::WorkbenchUtil::OpenRenderWindowPart(workbenchPartSite->GetPage(), false); if (nullptr == renderWindowPart) { // no render window available return; } } auto segmentation = dynamic_cast(dataNode->GetData()); if (nullptr != segmentation) { if (nullptr == baseRenderer) { renderWindowPart->SetSelectedPosition(centerPosition); mitk::RenderingManager::GetInstance()->InitializeViews(segmentation->GetTimeGeometry()); } else { renderWindowPart->SetSelectedPosition(centerPosition, baseRenderer->GetName()); mitk::RenderingManager::GetInstance()->InitializeView(baseRenderer->GetRenderWindow(), segmentation->GetTimeGeometry()); } } } } } QmitkLabelSetJumpToAction::QmitkLabelSetJumpToAction(QWidget* parent, berry::IWorkbenchPartSite::Pointer workbenchPartSite) : QAction(parent) , QmitkAbstractDataNodeAction(workbenchPartSite) { setText(tr("Jump to")); InitializeAction(); } QmitkLabelSetJumpToAction::QmitkLabelSetJumpToAction(QWidget* parent, berry::IWorkbenchPartSite* workbenchPartSite) : QAction(parent) , QmitkAbstractDataNodeAction(berry::IWorkbenchPartSite::Pointer(workbenchPartSite)) { setText(tr("Jump to")); InitializeAction(); } void QmitkLabelSetJumpToAction::InitializeAction() { connect(this, &QAction::triggered, this, &QmitkLabelSetJumpToAction::OnActionTriggered); } void QmitkLabelSetJumpToAction::OnActionTriggered(bool /*checked*/) { - if (m_WorkbenchPartSite.Expired()) + auto workbenchPartSite = m_WorkbenchPartSite.Lock(); + + if (workbenchPartSite.IsNull()) { return; } auto dataNode = GetSelectedNode(); if (dataNode.IsNull()) { return; } mitk::BaseRenderer::Pointer baseRenderer = GetBaseRenderer(); - LabelSetJumpToAction::Run(m_WorkbenchPartSite.Lock(), dataNode, baseRenderer); + LabelSetJumpToAction::Run(workbenchPartSite, dataNode, baseRenderer); } 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 c9e6cc8d40..08102751a3 100644 --- a/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkLesionInfoWidget.cpp +++ b/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkLesionInfoWidget.cpp @@ -1,492 +1,495 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ // semantic relations plugin #include "QmitkLesionInfoWidget.h" #include "QmitkDataNodeAddToSemanticRelationsAction.h" #include "QmitkFocusOnLesionAction.h" #include "QmitkSemanticRelationsNodeSelectionDialog.h" // semantic relations UI module #include // semantic relations module #include #include #include #include #include // segmentation #include #include // qt #include #include #include #include #include QmitkLesionInfoWidget::QmitkLesionInfoWidget(mitk::DataStorage* dataStorage, berry::IWorkbenchPartSite::Pointer workbenchPartSite, QWidget* parent /*= nullptr*/) : QWidget(parent) , m_DataStorage(dataStorage) , m_WorkbenchPartSite(workbenchPartSite) , m_SemanticRelationsDataStorageAccess(std::make_unique(dataStorage)) , m_SemanticRelationsIntegration(std::make_unique()) { Initialize(); } void QmitkLesionInfoWidget::Initialize() { m_Controls.setupUi(this); m_Controls.lesionTreeView->setAlternatingRowColors(true); m_Controls.lesionTreeView->setSelectionMode(QAbstractItemView::SingleSelection); m_Controls.lesionTreeView->setSelectionBehavior(QAbstractItemView::SelectRows); m_Controls.lesionTreeView->setContextMenuPolicy(Qt::CustomContextMenu); m_StorageModel = new QmitkLesionTreeModel(m_Controls.lesionTreeView); - if (m_DataStorage.IsExpired()) + auto dataStorage = m_DataStorage.Lock(); + + if (dataStorage.IsNull()) { return; } - auto dataStorage = m_DataStorage.Lock(); m_StorageModel->SetDataStorage(dataStorage); m_Controls.lesionTreeView->setModel(m_StorageModel); SetUpConnections(); } void QmitkLesionInfoWidget::SetUpConnections() { connect(m_StorageModel, &QmitkLesionTreeModel::ModelUpdated, this, &QmitkLesionInfoWidget::OnModelUpdated); connect(m_Controls.addLesionPushButton, &QPushButton::clicked, this, &QmitkLesionInfoWidget::OnAddLesionButtonClicked); connect(m_Controls.lesionTreeView->selectionModel(), &QItemSelectionModel::currentChanged, this, &QmitkLesionInfoWidget::OnSelectionChanged); connect(m_Controls.lesionTreeView, &QTreeView::customContextMenuRequested, this, &QmitkLesionInfoWidget::OnLesionListContextMenuRequested); } void QmitkLesionInfoWidget::SetCaseID(const mitk::SemanticTypes::CaseID& caseID) { m_CaseID = caseID; m_StorageModel->SetCaseID(caseID); } void QmitkLesionInfoWidget::SetDataNodeSelection(const QList& dataNodeSelection) { m_StorageModel->SetDataNodeSelection(dataNodeSelection); } ////////////////////////////////////////////////////////////////////////// // Implementation of the QT_SLOTS ////////////////////////////////////////////////////////////////////////// void QmitkLesionInfoWidget::OnModelUpdated() { m_Controls.lesionTreeView->expandAll(); int columns = m_Controls.lesionTreeView->model()->columnCount(); for (int i = 0; i < columns; ++i) { m_Controls.lesionTreeView->resizeColumnToContents(i); } } void QmitkLesionInfoWidget::OnAddLesionButtonClicked() { if (m_CaseID.empty()) { QMessageBox msgBox(QMessageBox::Warning, "No case ID set.", "In order to add a lesion, please specify the current case / patient."); msgBox.exec(); return; } mitk::SemanticTypes::Lesion newLesion = mitk::GenerateNewLesion(); try { m_SemanticRelationsIntegration->AddLesion(m_CaseID, newLesion); } catch (mitk::SemanticRelationException& e) { MITK_INFO << "Could not add a new lesion. " << e; } } void QmitkLesionInfoWidget::OnSelectionChanged(const QModelIndex& current, const QModelIndex& /*previous*/) { // only the UID is needed to identify a representing lesion QVariant data = m_StorageModel->data(current, Qt::UserRole); if (!data.canConvert()) { return; } auto lesion = data.value()->GetData().GetLesion(); if (false == mitk::SemanticRelationsInference::InstanceExists(m_CaseID, lesion)) { // no UID of a existing lesion found; cannot create a lesion return; } // if selected data nodes are set, reset to empty list to // hide "selected data nodes presence background highlighting" in the model if (!m_StorageModel->GetSelectedDataNodes().isEmpty()) { m_StorageModel->SetDataNodeSelection(QList()); } emit LesionSelectionChanged(lesion); } void QmitkLesionInfoWidget::OnLesionListContextMenuRequested(const QPoint& pos) { if (nullptr == m_SemanticRelationsIntegration) { return; } if (m_CaseID.empty()) { QMessageBox msgBox(QMessageBox::Warning, "No case ID set.", "In order to access the context menu entries a case ID has to be set."); msgBox.exec(); return; } QModelIndex index = m_Controls.lesionTreeView->indexAt(pos); if (!index.isValid()) { // no item clicked; cannot retrieve the current lesion return; } QVariant data = m_StorageModel->data(index, Qt::UserRole); mitk::SemanticTypes::Lesion selectedLesion; if (data.canConvert()) { selectedLesion = data.value()->GetData().GetLesion(); } else { return; } QMenu* menu = new QMenu(m_Controls.lesionTreeView); QAction* linkToSegmentation = new QAction("Link to segmentation", this); linkToSegmentation->setEnabled(true); connect(linkToSegmentation, &QAction::triggered, [this, selectedLesion] { OnLinkToSegmentation(selectedLesion); }); menu->addAction(linkToSegmentation); QAction* setLesionName = new QAction("Set lesion name", this); setLesionName->setEnabled(true); connect(setLesionName, &QAction::triggered, [this, selectedLesion] { OnSetLesionName(selectedLesion); }); menu->addAction(setLesionName); QAction* setLesionClass = new QAction("Set lesion class", this); setLesionClass->setEnabled(true); connect(setLesionClass, &QAction::triggered, [this, selectedLesion] { OnSetLesionClass(selectedLesion); }); menu->addAction(setLesionClass); QAction* createNewSegmentation = new QAction("Create new lesion", this); createNewSegmentation->setEnabled(true); connect(createNewSegmentation, &QAction::triggered, [this, selectedLesion] { OnCreateNewSegmentation(selectedLesion); }); menu->addAction(createNewSegmentation); QAction* removeLesion = new QAction("Remove lesion", this); removeLesion->setEnabled(true); connect(removeLesion, &QAction::triggered, [this, selectedLesion] { OnRemoveLesion(selectedLesion); }); menu->addAction(removeLesion); - if (!m_WorkbenchPartSite.Expired()) + auto workbenchPartSite = m_WorkbenchPartSite.Lock(); + + if (workbenchPartSite.IsNotNull()) { - QmitkFocusOnLesionAction* focusOnLesion = new QmitkFocusOnLesionAction(this, m_WorkbenchPartSite.Lock()); + QmitkFocusOnLesionAction* focusOnLesion = new QmitkFocusOnLesionAction(this, workbenchPartSite); focusOnLesion->SetDataStorage(m_DataStorage.Lock()); focusOnLesion->SetSelectedLesion(selectedLesion); menu->addAction(focusOnLesion); } menu->popup(QCursor::pos()); } void QmitkLesionInfoWidget::OnLinkToSegmentation(mitk::SemanticTypes::Lesion selectedLesion) { - if (m_DataStorage.IsExpired()) + auto dataStorage = m_DataStorage.Lock(); + + if (dataStorage.IsNull()) { return; } - auto dataStorage = m_DataStorage.Lock(); - QmitkSemanticRelationsNodeSelectionDialog* dialog = new QmitkSemanticRelationsNodeSelectionDialog(this, "Select segmentation to link to the selected lesion.", ""); dialog->setWindowTitle("Select segmentation node"); dialog->SetDataStorage(dataStorage); dialog->SetNodePredicate(mitk::NodePredicates::GetSegmentationPredicate()); dialog->SetSelectOnlyVisibleNodes(true); dialog->SetCaseID(m_CaseID); // set the last added segmentation node as pre-selected data node const mitk::DataNode* lastSegmentation = m_StorageModel->GetLastSegmentation(); QList selectedDataNodes; if (nullptr != lastSegmentation) { selectedDataNodes.push_back(const_cast(lastSegmentation)); dialog->SetCurrentSelection(selectedDataNodes); } int dialogReturnValue = dialog->exec(); if (QDialog::Rejected == dialogReturnValue) { return; } mitk::DataNode::Pointer selectedDataNode = nullptr; selectedDataNodes = dialog->GetSelectedNodes(); if (!selectedDataNodes.isEmpty()) { // only single selection allowed selectedDataNode = selectedDataNodes.front(); } if (nullptr == selectedDataNode || false == mitk::NodePredicates::GetSegmentationPredicate()->CheckNode(selectedDataNode)) { QMessageBox msgBox(QMessageBox::Warning, "No valid segmentation node selected.", "In order to link the selected lesion to a segmentation, please specify a valid segmentation node."); msgBox.exec(); return; } mitk::BaseData* baseData = selectedDataNode->GetData(); if (nullptr == baseData) { QMessageBox msgBox(QMessageBox::Warning, "No valid base data.", "In order to link the selected lesion to a segmentation, please specify a valid segmentation node."); msgBox.exec(); return; } LinkSegmentationToLesion(selectedDataNode, selectedLesion); } void QmitkLesionInfoWidget::OnSetLesionName(mitk::SemanticTypes::Lesion selectedLesion) { // 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; } selectedLesion.name = inputDialog->GetLineEditText().toStdString(); m_SemanticRelationsIntegration->OverwriteLesion(m_CaseID, selectedLesion); } void QmitkLesionInfoWidget::OnSetLesionClass(mitk::SemanticTypes::Lesion selectedLesion) { // 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 = mitk::SemanticRelationsInference::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(m_CaseID, newLesionClassType); 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_SemanticRelationsIntegration->OverwriteLesion(m_CaseID, selectedLesion); } void QmitkLesionInfoWidget::OnCreateNewSegmentation(mitk::SemanticTypes::Lesion selectedLesion) { - if (m_DataStorage.IsExpired()) + auto dataStorage = m_DataStorage.Lock(); + + if (dataStorage.IsNull()) { return; } - auto dataStorage = m_DataStorage.Lock(); - QmitkSemanticRelationsNodeSelectionDialog* dialog = new QmitkSemanticRelationsNodeSelectionDialog(this, "Select image to segment lesion on.", ""); dialog->setWindowTitle("Select image node"); dialog->SetDataStorage(dataStorage); dialog->SetNodePredicate(mitk::NodePredicates::GetImagePredicate()); dialog->SetSelectOnlyVisibleNodes(true); dialog->SetCaseID(m_CaseID); dialog->SetLesion(selectedLesion); 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 || false == mitk::NodePredicates::GetImagePredicate()->CheckNode(selectedDataNode)) { QMessageBox msgBox(QMessageBox::Warning, "No valid image node selected.", "In order to create a new segmentation, please specify a valid image node."); msgBox.exec(); return; } mitk::Image* selectedImage = dynamic_cast(selectedDataNode->GetData()); if (nullptr == selectedImage) { QMessageBox msgBox(QMessageBox::Warning, "No valid image.", "In order to create a new segmentation, please specify a valid image node."); msgBox.exec(); return; } mitk::LabelSetImage::Pointer segmentation = mitk::LabelSetImage::New(); try { segmentation->Initialize(selectedImage); } catch (mitk::Exception& e) { std::stringstream exceptionMessage; exceptionMessage << e; QMessageBox msgBox(QMessageBox::Warning, "Could not initialize segmentation.", "The segmentation could not be correctly initialized with the selected image geometry.\n" "Reason:\n" + QString::fromStdString(exceptionMessage.str())); msgBox.exec(); return; } auto segmentationDialog = new QmitkNewSegmentationDialog(this); segmentationDialog->setWindowTitle("New lesion segmentation"); dialogReturnValue = segmentationDialog->exec(); if (dialogReturnValue == QDialog::Rejected) { return; } QString segmentatioName = segmentationDialog->GetSegmentationName(); if (segmentatioName.isEmpty()) { segmentatioName = "Unnamed"; } segmentation->GetActiveLabelSet()->AddLabel(segmentatioName.toStdString(), segmentationDialog->GetColor()); mitk::DataNode::Pointer segmentationNode = mitk::DataNode::New(); segmentationNode->SetData(segmentation); segmentationNode->SetName(segmentatioName.toStdString()); dataStorage->Add(segmentationNode, selectedDataNode); LinkSegmentationToLesion(segmentationNode, selectedLesion); } void QmitkLesionInfoWidget::OnRemoveLesion(mitk::SemanticTypes::Lesion selectedLesion) { try { m_SemanticRelationsIntegration->RemoveLesion(m_CaseID, selectedLesion); } catch (const mitk::SemanticRelationException& e) { std::stringstream exceptionMessage; exceptionMessage << e; QMessageBox msgBox(QMessageBox::Warning, "Could not remove the selected lesion.", "The program wasn't able to correctly remove the selected lesion from the semantic relations model.\n" "Reason:\n" + QString::fromStdString(exceptionMessage.str())); msgBox.exec(); } } void QmitkLesionInfoWidget::LinkSegmentationToLesion(const mitk::DataNode* selectedDataNode, mitk::SemanticTypes::Lesion selectedLesion) { - if (m_DataStorage.IsExpired()) + auto dataStorage = m_DataStorage.Lock(); + + if (dataStorage.IsNull()) { return; } - auto dataStorage = m_DataStorage.Lock(); - // if the segmentation is not contained in the semantic relations, add it if (!mitk::SemanticRelationsInference::InstanceExists(selectedDataNode)) { try { AddToSemanticRelationsAction::Run(dataStorage, selectedDataNode); } catch (const mitk::SemanticRelationException& e) { std::stringstream exceptionMessage; exceptionMessage << e; QMessageBox msgBox(QMessageBox::Warning, "Could not link the selected lesion.", "The program wasn't able to correctly link the selected lesion with the selected segmentation.\n" "Reason:\n" + QString::fromStdString(exceptionMessage.str() + "\n")); msgBox.exec(); } } // link the segmentation try { m_SemanticRelationsIntegration->LinkSegmentationToLesion(selectedDataNode, selectedLesion); } catch (const mitk::SemanticRelationException& e) { std::stringstream exceptionMessage; exceptionMessage << e; QMessageBox msgBox(QMessageBox::Warning, "Could not link the selected lesion.", "The program wasn't able to correctly link the selected lesion with the selected segmentation.\n" "Reason:\n" + QString::fromStdString(exceptionMessage.str())); msgBox.exec(); } } diff --git a/Plugins/org.mitk.gui.qt.volumevisualization/src/internal/QmitkVolumeVisualizationView.cpp b/Plugins/org.mitk.gui.qt.volumevisualization/src/internal/QmitkVolumeVisualizationView.cpp index b4e5ff02af..0170559e19 100755 --- a/Plugins/org.mitk.gui.qt.volumevisualization/src/internal/QmitkVolumeVisualizationView.cpp +++ b/Plugins/org.mitk.gui.qt.volumevisualization/src/internal/QmitkVolumeVisualizationView.cpp @@ -1,239 +1,243 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #include "QmitkVolumeVisualizationView.h" #include #include #include #include #include #include #include #include #include #include #include #include const std::string QmitkVolumeVisualizationView::VIEW_ID = "org.mitk.views.volumevisualization"; enum { DEFAULT_RENDERMODE = 0, RAYCAST_RENDERMODE = 1, GPU_RENDERMODE = 2 }; QmitkVolumeVisualizationView::QmitkVolumeVisualizationView() : QmitkAbstractView() , m_Controls(nullptr) { } void QmitkVolumeVisualizationView::SetFocus() { } void QmitkVolumeVisualizationView::CreateQtPartControl(QWidget* parent) { m_Controls = new Ui::QmitkVolumeVisualizationViewControls; m_Controls->setupUi(parent); m_Controls->volumeSelectionWidget->SetDataStorage(GetDataStorage()); m_Controls->volumeSelectionWidget->SetNodePredicate(mitk::NodePredicateAnd::New( mitk::TNodePredicateDataType::New(), mitk::NodePredicateOr::New(mitk::NodePredicateDimension::New(3), mitk::NodePredicateDimension::New(4)), mitk::NodePredicateNot::New(mitk::NodePredicateProperty::New("helper object")))); m_Controls->volumeSelectionWidget->SetSelectionIsOptional(true); m_Controls->volumeSelectionWidget->SetEmptyInfo(QString("Please select a 3D / 4D image volume")); m_Controls->volumeSelectionWidget->SetPopUpTitel(QString("Select image volume")); // Fill the transfer function presets in the generator widget std::vector names; mitk::TransferFunctionInitializer::GetPresetNames(names); for (const auto& name : names) { m_Controls->transferFunctionGeneratorWidget->AddPreset(QString::fromStdString(name)); } // see vtkVolumeMapper::BlendModes m_Controls->blendMode->addItem("Composite", vtkVolumeMapper::COMPOSITE_BLEND); m_Controls->blendMode->addItem("Maximum intensity", vtkVolumeMapper::MAXIMUM_INTENSITY_BLEND); m_Controls->blendMode->addItem("Minimum intensity", vtkVolumeMapper::MINIMUM_INTENSITY_BLEND); m_Controls->blendMode->addItem("Average intensity", vtkVolumeMapper::AVERAGE_INTENSITY_BLEND); m_Controls->blendMode->addItem("Additive", vtkVolumeMapper::ADDITIVE_BLEND); connect(m_Controls->volumeSelectionWidget, &QmitkSingleNodeSelectionWidget::CurrentSelectionChanged, this, &QmitkVolumeVisualizationView::OnCurrentSelectionChanged); connect(m_Controls->enableRenderingCB, SIGNAL(toggled(bool)), this, SLOT(OnEnableRendering(bool))); connect(m_Controls->blendMode, SIGNAL(activated(int)), this, SLOT(OnBlendMode(int))); connect(m_Controls->transferFunctionGeneratorWidget, SIGNAL(SignalUpdateCanvas()), m_Controls->transferFunctionWidget, SLOT(OnUpdateCanvas())); connect(m_Controls->transferFunctionGeneratorWidget, SIGNAL(SignalTransferFunctionModeChanged(int)), SLOT(OnMitkInternalPreset(int))); m_Controls->enableRenderingCB->setEnabled(false); m_Controls->blendMode->setEnabled(false); m_Controls->transferFunctionWidget->setEnabled(false); m_Controls->transferFunctionGeneratorWidget->setEnabled(false); m_Controls->volumeSelectionWidget->SetAutoSelectNewNodes(true); this->m_TimePointChangeListener.RenderWindowPartActivated(this->GetRenderWindowPart()); connect(&m_TimePointChangeListener, &QmitkSliceNavigationListener::SelectedTimePointChanged, this, &QmitkVolumeVisualizationView::OnSelectedTimePointChanged); } void QmitkVolumeVisualizationView::RenderWindowPartActivated(mitk::IRenderWindowPart *renderWindowPart) { this->m_TimePointChangeListener.RenderWindowPartActivated(renderWindowPart); } void QmitkVolumeVisualizationView::RenderWindowPartDeactivated(mitk::IRenderWindowPart *renderWindowPart) { this->m_TimePointChangeListener.RenderWindowPartDeactivated(renderWindowPart); } void QmitkVolumeVisualizationView::OnMitkInternalPreset(int mode) { - if (m_SelectedNode.IsExpired()) + auto node = m_SelectedNode.Lock(); + + if (node.IsNull()) { return; } - auto node = m_SelectedNode.Lock(); mitk::TransferFunctionProperty::Pointer transferFuncProp; if (node->GetProperty(transferFuncProp, "TransferFunction")) { // first item is only information if (--mode == -1) return; // -- Creat new TransferFunction mitk::TransferFunctionInitializer::Pointer tfInit = mitk::TransferFunctionInitializer::New(transferFuncProp->GetValue()); tfInit->SetTransferFunctionMode(mode); RequestRenderWindowUpdate(); m_Controls->transferFunctionWidget->OnUpdateCanvas(); } } void QmitkVolumeVisualizationView::OnCurrentSelectionChanged(QList nodes) { m_SelectedNode = nullptr; if (nodes.empty() || nodes.front().IsNull()) { UpdateInterface(); return; } auto selectedNode = nodes.front(); auto image = dynamic_cast(selectedNode->GetData()); if (nullptr != image) { m_SelectedNode = selectedNode; } UpdateInterface(); } void QmitkVolumeVisualizationView::OnEnableRendering(bool state) { - if (m_SelectedNode.IsExpired()) + auto selectedNode = m_SelectedNode.Lock(); + + if (selectedNode.IsNull()) { return; } - m_SelectedNode.Lock()->SetProperty("volumerendering", mitk::BoolProperty::New(state)); + selectedNode->SetProperty("volumerendering", mitk::BoolProperty::New(state)); UpdateInterface(); RequestRenderWindowUpdate(); } void QmitkVolumeVisualizationView::OnBlendMode(int index) { - if (m_SelectedNode.IsExpired()) + auto selectedNode = m_SelectedNode.Lock(); + + if (selectedNode.IsNull()) return; - auto selectedNode = m_SelectedNode.Lock(); int mode = m_Controls->blendMode->itemData(index).toInt(); selectedNode->SetProperty("volumerendering.blendmode", mitk::IntProperty::New(mode)); this->RequestRenderWindowUpdate(); } void QmitkVolumeVisualizationView::OnSelectedTimePointChanged(const mitk::TimePointType & /*newTimePoint*/) { this->UpdateInterface(); } void QmitkVolumeVisualizationView::UpdateInterface() { if (m_SelectedNode.IsExpired()) { // turnoff all m_Controls->enableRenderingCB->setChecked(false); m_Controls->enableRenderingCB->setEnabled(false); m_Controls->blendMode->setCurrentIndex(0); m_Controls->blendMode->setEnabled(false); m_Controls->transferFunctionWidget->SetDataNode(nullptr); m_Controls->transferFunctionWidget->setEnabled(false); m_Controls->transferFunctionGeneratorWidget->SetDataNode(nullptr); m_Controls->transferFunctionGeneratorWidget->setEnabled(false); return; } bool enabled = false; auto selectedNode = m_SelectedNode.Lock(); selectedNode->GetBoolProperty("volumerendering", enabled); m_Controls->enableRenderingCB->setEnabled(true); m_Controls->enableRenderingCB->setChecked(enabled); if (!enabled) { // turnoff all except volumerendering checkbox m_Controls->blendMode->setCurrentIndex(0); m_Controls->blendMode->setEnabled(false); m_Controls->transferFunctionWidget->SetDataNode(nullptr); m_Controls->transferFunctionWidget->setEnabled(false); m_Controls->transferFunctionGeneratorWidget->SetDataNode(nullptr); m_Controls->transferFunctionGeneratorWidget->setEnabled(false); return; } // otherwise we can activate em all m_Controls->blendMode->setEnabled(true); // Determine Combo Box mode int blendMode; if (selectedNode->GetIntProperty("volumerendering.blendmode", blendMode)) m_Controls->blendMode->setCurrentIndex(blendMode); auto time = this->GetRenderWindowPart()->GetTimeNavigationController()->GetSelectedTimeStep(); m_Controls->transferFunctionWidget->SetDataNode(selectedNode, time); m_Controls->transferFunctionWidget->setEnabled(true); m_Controls->transferFunctionGeneratorWidget->SetDataNode(selectedNode, time); m_Controls->transferFunctionGeneratorWidget->setEnabled(true); }