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 d8b468fc90..f335db40d2 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,261 +1,265 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #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 (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.IsNull()) + if (m_SelectedNode.IsExpired()) return; + auto selectedNode = m_SelectedNode.Lock(); + mitk::TransferFunctionProperty::Pointer transferFunctionProp = - dynamic_cast(m_SelectedNode->GetProperty("TransferFunction")); + 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; - if (m_SelectedNode2.IsNotNull()) + if (!m_SelectedNode2.IsExpired()) { RGBAImageResult = - CImageProcessor.convertWithBinaryToRGBAImage(dynamic_cast(m_SelectedNode->GetData()), - dynamic_cast(m_SelectedNode2->GetData()), + CImageProcessor.convertWithBinaryToRGBAImage(dynamic_cast(selectedNode->GetData()), + dynamic_cast(selectedNode2.Lock()->GetData()), tf); } else { - RGBAImageResult = CImageProcessor.convertToRGBAImage(dynamic_cast(m_SelectedNode->GetData()), tf); + 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(m_SelectedNode->GetName() + "_RGBA"); + 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.IsNull()) + if (m_SelectedNode.IsExpired()) return; + auto selectedNode = m_SelectedNode.Lock(); + mitk::TransferFunctionProperty::Pointer transferFunctionProp = - dynamic_cast(m_SelectedNode->GetProperty("TransferFunction")); + 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; - if (m_SelectedNode2.IsNotNull()) + if (!m_SelectedNode2.IsExpired()) { RGBAImageResult = - CImageProcessor.convertWithBinaryAndColorToRGBAImage(dynamic_cast(m_SelectedNode->GetData()), - dynamic_cast(m_SelectedNode2->GetData()), + CImageProcessor.convertWithBinaryAndColorToRGBAImage(dynamic_cast(selectedNode->GetData()), + dynamic_cast(m_SelectedNode2.Lock()->GetData()), tf, m_Color); } else { - RGBAImageResult = CImageProcessor.convertToRGBAImage(dynamic_cast(m_SelectedNode->GetData()), tf); + 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(m_SelectedNode->GetName() + "_RGBA"); + 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.IsNull()) + if (m_SelectedNode.IsExpired()) return; - if (m_SelectedNode2.IsNull()) + if (m_SelectedNode2.IsExpired()) return; mitk::mitkColourImageProcessor CImageProcessor; mitk::Image::Pointer RGBAImageResult; - RGBAImageResult = CImageProcessor.combineRGBAImage(dynamic_cast(m_SelectedNode->GetData()), - dynamic_cast(m_SelectedNode2->GetData())); + RGBAImageResult = CImageProcessor.combineRGBAImage(dynamic_cast(m_SelectedNode.Lock()->GetData()), + dynamic_cast(m_SelectedNode2.Lock()->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 316564e60a..051df5b053 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,248 +1,250 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "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.IsNull() || m_SelectedDataNode.GetPointer() == m_OverlayNode.GetPointer()) + 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->GetData()); + 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.IsNotNull()) + if (!m_SelectedDataNode.IsExpired()) { - mitk::Image *image = dynamic_cast(m_SelectedDataNode->GetData()); + mitk::Image *image = dynamic_cast(m_SelectedDataNode.Lock()->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.IsNotNull()) + if (!m_SelectedDataNode.IsExpired()) { - mitk::Image *image = dynamic_cast(m_SelectedDataNode->GetData()); + mitk::Image *image = dynamic_cast(m_SelectedDataNode.Lock()->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; + return m_SelectedDataNode.Lock(); } void QmitkVolumetryView::UpdateSlider() { - if (m_SelectedDataNode.IsNotNull() && dynamic_cast(m_SelectedDataNode->GetData())) + if (!m_SelectedDataNode.IsExpired() && dynamic_cast(m_SelectedDataNode.Lock()->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.IsNotNull()) + if (!m_SelectedDataNode.IsExpired()) { + auto selectedDataNode = m_SelectedDataNode.Lock(); + m_OverlayNode = mitk::DataNode::New(); mitk::StringProperty::Pointer nameProp = mitk::StringProperty::New("volume threshold overlay image"); - m_OverlayNode->SetProperty("reslice interpolation", m_SelectedDataNode->GetProperty("reslice interpolation")); + m_OverlayNode->SetProperty("reslice interpolation", selectedDataNode->GetProperty("reslice interpolation")); m_OverlayNode->SetProperty("name", nameProp); - m_OverlayNode->SetData(m_SelectedDataNode->GetData()); + m_OverlayNode->SetData(selectedDataNode->GetData()); m_OverlayNode->SetColor(0.0, 1.0, 0.0); m_OverlayNode->SetOpacity(.25); int layer = 0; - m_SelectedDataNode->GetIntProperty("layer", layer); + 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->GetData()); + 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/mitkNonBlockingAlgorithm.cpp b/Modules/AlgorithmsExt/src/mitkNonBlockingAlgorithm.cpp index 726910a162..7eb4797589 100644 --- a/Modules/AlgorithmsExt/src/mitkNonBlockingAlgorithm.cpp +++ b/Modules/AlgorithmsExt/src/mitkNonBlockingAlgorithm.cpp @@ -1,207 +1,207 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkNonBlockingAlgorithm.h" #include "mitkCallbackFromGUIThread.h" #include "mitkDataStorage.h" #include namespace mitk { NonBlockingAlgorithm::NonBlockingAlgorithm() : m_ThreadID(-1), m_UpdateRequests(0), m_KillRequest(false) { m_ParameterListMutex = itk::FastMutexLock::New(); m_Parameters = PropertyList::New(); m_MultiThreader = itk::MultiThreader::New(); } NonBlockingAlgorithm::~NonBlockingAlgorithm() {} void mitk::NonBlockingAlgorithm::SetDataStorage(DataStorage &storage) { m_DataStorage = &storage; } - DataStorage *mitk::NonBlockingAlgorithm::GetDataStorage() { return m_DataStorage; } + DataStorage *mitk::NonBlockingAlgorithm::GetDataStorage() { return m_DataStorage.Lock(); } void NonBlockingAlgorithm::Initialize(const NonBlockingAlgorithm *itkNotUsed(other)) { // define one input, one output basedata object // some basedata input - image, surface, whatever BaseData::Pointer input; SetPointerParameter("Input", input); // some basedata output BaseData::Pointer output; SetPointerParameter("Output", output); } void NonBlockingAlgorithm::SetPointerParameter(const char *parameter, BaseData *value) { m_ParameterListMutex->Lock(); m_Parameters->SetProperty(parameter, SmartPointerProperty::New(value)); m_ParameterListMutex->Unlock(); } void NonBlockingAlgorithm::DefineTriggerParameter(const char *parameter) { BaseProperty *value = m_Parameters->GetProperty(parameter); if (value && m_TriggerPropertyConnections.find(parameter) == m_TriggerPropertyConnections.end()) { itk::ReceptorMemberCommand::Pointer command = itk::ReceptorMemberCommand::New(); command->SetCallbackFunction(this, &NonBlockingAlgorithm::TriggerParameterModified); m_TriggerPropertyConnections[parameter] = value->AddObserver(itk::ModifiedEvent(), command); } } void NonBlockingAlgorithm::UnDefineTriggerParameter(const char *parameter) { auto iter = m_TriggerPropertyConnections.find(parameter); if (iter != m_TriggerPropertyConnections.end()) { BaseProperty *value = m_Parameters->GetProperty(parameter); MITK_ERROR(!value) << "NonBlockingAlgorithm::UnDefineTriggerProperty() in bad state." << std::endl; ; value->RemoveObserver(m_TriggerPropertyConnections[parameter]); m_TriggerPropertyConnections.erase(iter); } } void NonBlockingAlgorithm::Reset() { Initialize(); } void NonBlockingAlgorithm::StartBlockingAlgorithm() { StartAlgorithm(); StopAlgorithm(); } void NonBlockingAlgorithm::StartAlgorithm() { if (!ReadyToRun()) return; // let algorithm check if all input/parameters are ok if (m_KillRequest) return; // someone wants us to die m_ParameterListMutex->Lock(); m_ThreadParameters.m_Algorithm = this; ++m_UpdateRequests; m_ParameterListMutex->Unlock(); if (m_ThreadID != -1) // thread already running. But something obviously wants us to recalculate the output { return; // thread already running } // spawn a thread that calls ThreadedUpdateFunction(), and ThreadedUpdateFinished() on us itk::ThreadFunctionType fpointer = &StaticNonBlockingAlgorithmThread; m_ThreadID = m_MultiThreader->SpawnThread(fpointer, &m_ThreadParameters); } void NonBlockingAlgorithm::StopAlgorithm() { if (m_ThreadID == -1) return; // thread not running m_MultiThreader->TerminateThread(m_ThreadID); // waits for the thread to terminate on its own } // a static function to call a member of NonBlockingAlgorithm from inside an ITK thread ITK_THREAD_RETURN_TYPE NonBlockingAlgorithm::StaticNonBlockingAlgorithmThread(void *param) { // itk::MultiThreader provides an itk::MultiThreader::ThreadInfoStruct as parameter auto *itkmttis = static_cast(param); // we need the UserData part of that structure auto *flsp = static_cast(itkmttis->UserData); NonBlockingAlgorithm::Pointer algorithm = flsp->m_Algorithm; // this UserData tells us, which BubbleTool's method to call if (!algorithm) { return ITK_THREAD_RETURN_VALUE; } algorithm->m_ParameterListMutex->Lock(); while (algorithm->m_UpdateRequests > 0) { algorithm->m_UpdateRequests = 0; algorithm->m_ParameterListMutex->Unlock(); // actually call the methods that do the work if (algorithm->ThreadedUpdateFunction()) // returns a bool for success/failure { itk::ReceptorMemberCommand::Pointer command = itk::ReceptorMemberCommand::New(); command->SetCallbackFunction(algorithm, &NonBlockingAlgorithm::ThreadedUpdateSuccessful); CallbackFromGUIThread::GetInstance()->CallThisFromGUIThread(command); // algorithm->ThreadedUpdateSuccessful(); } else { itk::ReceptorMemberCommand::Pointer command = itk::ReceptorMemberCommand::New(); command->SetCallbackFunction(algorithm, &NonBlockingAlgorithm::ThreadedUpdateFailed); CallbackFromGUIThread::GetInstance()->CallThisFromGUIThread(command); // algorithm->ThreadedUpdateFailed(); } algorithm->m_ParameterListMutex->Lock(); } algorithm->m_ParameterListMutex->Unlock(); return ITK_THREAD_RETURN_VALUE; } void NonBlockingAlgorithm::TriggerParameterModified(const itk::EventObject &) { StartAlgorithm(); } bool NonBlockingAlgorithm::ReadyToRun() { return true; // default is always ready } bool NonBlockingAlgorithm::ThreadedUpdateFunction() { return true; } // called from gui thread void NonBlockingAlgorithm::ThreadedUpdateSuccessful(const itk::EventObject &) { ThreadedUpdateSuccessful(); m_ParameterListMutex->Lock(); m_ThreadID = -1; // tested before starting m_ParameterListMutex->Unlock(); m_ThreadParameters.m_Algorithm = nullptr; } void NonBlockingAlgorithm::ThreadedUpdateSuccessful() { // notify observers that a result is ready InvokeEvent(ResultAvailable(this)); } // called from gui thread void NonBlockingAlgorithm::ThreadedUpdateFailed(const itk::EventObject &) { ThreadedUpdateFailed(); m_ParameterListMutex->Lock(); m_ThreadID = -1; // tested before starting m_ParameterListMutex->Unlock(); m_ThreadParameters.m_Algorithm = nullptr; // delete } void NonBlockingAlgorithm::ThreadedUpdateFailed() { // notify observers that something went wrong InvokeEvent(ProcessingError(this)); } } // namespace diff --git a/Modules/AlgorithmsExt/src/mitkSimpleHistogram.cpp b/Modules/AlgorithmsExt/src/mitkSimpleHistogram.cpp index a569d0f71b..d4f9fffcba 100644 --- a/Modules/AlgorithmsExt/src/mitkSimpleHistogram.cpp +++ b/Modules/AlgorithmsExt/src/mitkSimpleHistogram.cpp @@ -1,315 +1,315 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkSimpleHistogram.h" #include "mitkImageReadAccessor.h" #include "mitkSimpleUnstructuredGridHistogram.h" #include "mitkUnstructuredGrid.h" namespace mitk { void SimpleImageHistogram::ComputeFromBaseData(BaseData *src) { valid = false; // check if input is valid if (src == nullptr) return; auto *source = dynamic_cast(src); if (source == nullptr) return; else if (source->IsEmpty()) return; // dummy histogram { min = 0; max = 1; first = 0; last = 1; } { int typInt = 0; { const int typ = source->GetPixelType().GetComponentType(); if (typ == itk::ImageIOBase::UCHAR) typInt = 0; else if (typ == itk::ImageIOBase::CHAR) typInt = 1; else if (typ == itk::ImageIOBase::USHORT) typInt = 2; else if (typ == itk::ImageIOBase::SHORT) typInt = 3; else if (typ == itk::ImageIOBase::INT) typInt = 4; else if (typ == itk::ImageIOBase::UINT) typInt = 5; else if (typ == itk::ImageIOBase::LONG) typInt = 6; else if (typ == itk::ImageIOBase::ULONG) typInt = 7; else if (typ == itk::ImageIOBase::FLOAT) typInt = 8; else if (typ == itk::ImageIOBase::DOUBLE) typInt = 9; else { MITK_WARN << "Pixel type not supported by SimpleImageHistogram"; return; } } first = -32768; last = 65535; // support at least full signed and unsigned short range if (histogram) delete histogram; histogram = new CountType[last - first + 1]; memset(histogram, 0, sizeof(CountType) * (last - first + 1)); highest = 0; max = first - 1; min = last + 1; unsigned int num = 1; for (unsigned int r = 0; r < source->GetDimension(); r++) num *= source->GetDimension(r); // MITK_INFO << "building histogramm of integer image: 0=" << source->GetDimension(0) << " 1=" << // source->GetDimension(1) << " 2=" << source->GetDimension(2) << " 3=" << source->GetDimension(3); ImageReadAccessor sourceAcc(source); const void *src = sourceAcc.GetData(); do { int value = 0; switch (typInt) { case 0: { auto *t = (unsigned char *)src; value = *t++; src = (void *)t; } break; case 1: { auto *t = (signed char *)src; value = *t++; src = (void *)t; } break; case 2: { auto *t = (unsigned short *)src; value = *t++; src = (void *)t; } break; case 3: { auto *t = (signed short *)src; value = *t++; src = (void *)t; } break; case 4: { auto *t = (signed int *)src; value = *t++; src = (void *)t; } break; case 5: { auto *t = (unsigned int *)src; value = *t++; src = (void *)t; } break; case 6: { auto *t = (signed long *)src; value = *t++; src = (void *)t; } break; case 7: { auto *t = (unsigned long *)src; value = *t++; src = (void *)t; } break; case 8: { auto *t = (float *)src; value = *t++; src = (void *)t; } break; case 9: { auto *t = (double *)src; value = *t++; src = (void *)t; } break; } if (value >= first && value <= last) { if (value < min) min = value; if (value > max) max = value; CountType tmp = ++histogram[value - first]; if (tmp > highest) highest = tmp; } } while (--num); MITK_INFO << "histogramm computed: min=" << min << " max=" << max << " highestBin=" << highest << " samples=" << num; } invLogHighest = 1.0 / log(double(highest)); valid = true; } bool SimpleImageHistogram::GetValid() { return valid; } float SimpleImageHistogram::GetRelativeBin(double left, double right) const { if (!valid) return 0.0f; int iLeft = floorf(left); int iRight = ceilf(right); /* double sum = 0; for( int r = 0 ; r < 256 ; r++) { int pos = left + (right-left) * r/255.0; int posInArray = floorf(pos+0.5f) - first; sum += float(log(double(histogram[posInArray]))); } sum /= 256.0; return float(sum*invLogHighest); */ CountType maximum = 0; for (int i = iLeft; i <= iRight; i++) { int posInArray = i - first; if (histogram[posInArray] > maximum) maximum = histogram[posInArray]; } return float(log(double(maximum)) * invLogHighest); } class ImageHistogramCacheElement : public SimpleHistogramCache::Element { public: void ComputeFromBaseData(BaseData *baseData) override { histogram.ComputeFromBaseData(baseData); } SimpleHistogram *GetHistogram() override { return &histogram; } SimpleImageHistogram histogram; }; class UnstructuredGridHistogramCacheElement : public SimpleHistogramCache::Element { public: void ComputeFromBaseData(BaseData *baseData) override { histogram.ComputeFromBaseData(baseData); } SimpleHistogram *GetHistogram() override { return &histogram; } SimpleUnstructuredGridHistogram histogram; }; SimpleHistogram *SimpleHistogramCache::operator[](BaseData::Pointer sp_BaseData) { BaseData *p_BaseData = sp_BaseData.GetPointer(); if (!p_BaseData) { MITK_WARN << "SimpleHistogramCache::operator[] with null base data called"; return nullptr; } Element *elementToUpdate = nullptr; bool first = true; for (auto iter = cache.begin(); iter != cache.end(); iter++) { Element *e = *iter; - BaseData *p_tmp = e->baseData.GetPointer(); + BaseData *p_tmp = e->baseData.Lock(); if (p_tmp == p_BaseData) { if (!first) { cache.erase(iter); cache.push_front(e); } if (p_BaseData->GetMTime() > e->m_LastUpdateTime.GetMTime()) { elementToUpdate = e; goto recomputeElement; } // MITK_INFO << "using a cached histogram"; return e->GetHistogram(); } first = false; } if (dynamic_cast(p_BaseData)) { elementToUpdate = new ImageHistogramCacheElement(); } else if (dynamic_cast(p_BaseData)) { elementToUpdate = new UnstructuredGridHistogramCacheElement(); } else { MITK_WARN << "not supported: " << p_BaseData->GetNameOfClass(); } elementToUpdate->baseData = p_BaseData; cache.push_front(elementToUpdate); TrimCache(); recomputeElement: // MITK_INFO << "computing a new histogram"; elementToUpdate->ComputeFromBaseData(p_BaseData); elementToUpdate->m_LastUpdateTime.Modified(); return elementToUpdate->GetHistogram(); } SimpleHistogramCache::Element::~Element() {} } diff --git a/Modules/Core/src/DataManagement/mitkNodePredicateFirstLevel.cpp b/Modules/Core/src/DataManagement/mitkNodePredicateFirstLevel.cpp index 12099d8149..e239d9c322 100644 --- a/Modules/Core/src/DataManagement/mitkNodePredicateFirstLevel.cpp +++ b/Modules/Core/src/DataManagement/mitkNodePredicateFirstLevel.cpp @@ -1,37 +1,37 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "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.IsNull()) + if (m_DataStorage.IsExpired()) throw std::invalid_argument("NodePredicateFirstLevel: DataStorage is invalid"); - mitk::DataStorage::SetOfObjects::ConstPointer list = m_DataStorage->GetSources(node, nullptr, true); + mitk::DataStorage::SetOfObjects::ConstPointer list = m_DataStorage.Lock()->GetSources(node, nullptr, true); return (list->Size() == 0); } diff --git a/Modules/Core/src/DataManagement/mitkNodePredicateSource.cpp b/Modules/Core/src/DataManagement/mitkNodePredicateSource.cpp index 0bdd4a3580..3e9c17876d 100644 --- a/Modules/Core/src/DataManagement/mitkNodePredicateSource.cpp +++ b/Modules/Core/src/DataManagement/mitkNodePredicateSource.cpp @@ -1,39 +1,39 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkNodePredicateSource.h" mitk::NodePredicateSource::NodePredicateSource(mitk::DataNode *n, bool allsources, mitk::DataStorage *ds) : NodePredicateBase(), m_BaseNode(n), m_SearchAllSources(allsources), m_DataStorage(ds) { } mitk::NodePredicateSource::~NodePredicateSource() { } bool mitk::NodePredicateSource::CheckNode(const mitk::DataNode *childNode) const { if (m_DataStorage && m_BaseNode) { const mitk::DataStorage::SetOfObjects::STLContainerType sources = - m_DataStorage->GetSources(childNode, nullptr, !m_SearchAllSources)->CastToSTLConstContainer(); + m_DataStorage.Lock()->GetSources(childNode, nullptr, !m_SearchAllSources)->CastToSTLConstContainer(); - return std::find(sources.cbegin(), sources.cend(), m_BaseNode) != sources.cend(); + return std::find(sources.cbegin(), sources.cend(), m_BaseNode.Lock()) != sources.cend(); } return false; } diff --git a/Modules/Core/src/Interactions/mitkDataInteractor.cpp b/Modules/Core/src/Interactions/mitkDataInteractor.cpp index 7b48394a4a..2e4621aa55 100644 --- a/Modules/Core/src/Interactions/mitkDataInteractor.cpp +++ b/Modules/Core/src/Interactions/mitkDataInteractor.cpp @@ -1,100 +1,102 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkDataInteractor.h" #include "mitkDataNode.h" #include "mitkStateMachineState.h" // 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.IsNotNull()) + if (!m_DataNode.IsExpired()) { - if (m_DataNode->GetDataInteractor() == this) - m_DataNode->SetDataInteractor(nullptr); + auto dataNode = m_DataNode.Lock(); + + if (dataNode->GetDataInteractor() == this) + dataNode->SetDataInteractor(nullptr); } } mitk::DataNode *mitk::DataInteractor::GetDataNode() const { - return m_DataNode; + return m_DataNode.Lock(); } void mitk::DataInteractor::SetDataNode(DataNode *dataNode) { if (dataNode == m_DataNode) return; - if (m_DataNode.IsNotNull()) - m_DataNode->SetDataInteractor(nullptr); + if (!m_DataNode.IsExpired()) + m_DataNode.Lock()->SetDataInteractor(nullptr); m_DataNode = dataNode; if (dataNode != nullptr) - m_DataNode->SetDataInteractor(this); + m_DataNode.Lock()->SetDataInteractor(this); this->DataNodeChanged(); } int mitk::DataInteractor::GetLayer() const { int layer = -1; - if (m_DataNode.IsNotNull()) - m_DataNode->GetIntProperty("layer", layer); + if (!m_DataNode.IsExpired()) + m_DataNode.Lock()->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 66ec2b5e8c..1683621cf8 100644 --- a/Modules/Core/src/Interactions/mitkDispatcher.cpp +++ b/Modules/Core/src/Interactions/mitkDispatcher.cpp @@ -1,281 +1,284 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkDispatcher.h" #include "mitkInteractionEvent.h" #include "mitkInteractionEventObserver.h" #include "mitkInternalEvent.h" #include "usGetModuleContext.h" namespace { struct cmp { - bool operator()(mitk::DataInteractor *d1, mitk::DataInteractor *d2) { return (d1->GetLayer() > d2->GetLayer()); } + 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).IsNull() || (*it)->GetDataNode() == nullptr || (*it)->GetDataNode() == dataNode) + if ((*it).IsExpired() || (*it).Lock()->GetDataNode() == nullptr || (*it).Lock()->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; } } switch (m_ProcessingMode) { case CONNECTEDMOUSEACTION: // finished connected mouse action if (std::strcmp(p->GetNameOfClass(), "MouseReleaseEvent") == 0) { m_ProcessingMode = REGULAR; - if (m_SelectedInteractor.IsNotNull()) - eventIsHandled = m_SelectedInteractor->HandleEvent(event, m_SelectedInteractor->GetDataNode()); + if (!m_SelectedInteractor.IsExpired()) + eventIsHandled = m_SelectedInteractor.Lock()->HandleEvent(event, m_SelectedInteractor.Lock()->GetDataNode()); m_SelectedInteractor = nullptr; } // give event to selected interactor - if (eventIsHandled == false && m_SelectedInteractor.IsNotNull()) - eventIsHandled = m_SelectedInteractor->HandleEvent(event, m_SelectedInteractor->GetDataNode()); + if (eventIsHandled == false && !m_SelectedInteractor.IsExpired()) + eventIsHandled = m_SelectedInteractor.Lock()->HandleEvent(event, m_SelectedInteractor.Lock()->GetDataNode()); break; case GRABINPUT: - if (m_SelectedInteractor.IsNotNull()) + if (!m_SelectedInteractor.IsExpired()) { - eventIsHandled = m_SelectedInteractor->HandleEvent(event, m_SelectedInteractor->GetDataNode()); - SetEventProcessingMode(m_SelectedInteractor); + eventIsHandled = m_SelectedInteractor.Lock()->HandleEvent(event, m_SelectedInteractor.Lock()->GetDataNode()); + SetEventProcessingMode(m_SelectedInteractor.Lock()); } break; case PREFERINPUT: - if (m_SelectedInteractor.IsNotNull() && - m_SelectedInteractor->HandleEvent(event, m_SelectedInteractor->GetDataNode()) == true) + if (!m_SelectedInteractor.IsExpired() && + m_SelectedInteractor.Lock()->HandleEvent(event, m_SelectedInteractor.Lock()->GetDataNode()) == true) { - SetEventProcessingMode(m_SelectedInteractor); + SetEventProcessingMode(m_SelectedInteractor.Lock()); 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) event->GetSender()->GetRenderingManager()->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).IsNotNull() && (*it)->HandleEvent(event, (*it)->GetDataNode())) + if (!(*it).IsExpired() && (*it).Lock()->HandleEvent(event, (*it).Lock()->GetDataNode())) { // Interactor can be deleted during HandleEvent(), so check it again - if ((*it).IsNotNull()) + if (!(*it).IsExpired()) { // if an event is handled several properties are checked, in order to determine the processing mode of the // dispatcher - SetEventProcessingMode(*it); + SetEventProcessingMode((*it).Lock()); } 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).IsNull()) + if ((*it).IsExpired()) { it = m_Interactors.erase(it); } else { - DataNode::Pointer node = (*it)->GetDataNode(); + DataNode::Pointer node = (*it).Lock()->GetDataNode(); if (node.IsNull()) { it = m_Interactors.erase(it); } else { DataInteractor::Pointer interactor = node->GetDataInteractor(); - if (interactor != it->GetPointer()) + 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/mitkDisplayCoordinateOperation.cpp b/Modules/Core/src/Interactions/mitkDisplayCoordinateOperation.cpp index cca852cf88..a9e5c3e53c 100644 --- a/Modules/Core/src/Interactions/mitkDisplayCoordinateOperation.cpp +++ b/Modules/Core/src/Interactions/mitkDisplayCoordinateOperation.cpp @@ -1,69 +1,69 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkDisplayCoordinateOperation.h" mitk::DisplayCoordinateOperation::DisplayCoordinateOperation(mitk::OperationType operationType, mitk::BaseRenderer *renderer, const mitk::Point2D &startDisplayCoordinate, const mitk::Point2D &lastDisplayCoordinate, const mitk::Point2D ¤tDisplayCoordinate) : mitk::Operation(operationType), m_Renderer(renderer), m_StartDisplayCoordinate(startDisplayCoordinate), m_LastDisplayCoordinate(lastDisplayCoordinate), m_CurrentDisplayCoordinate(currentDisplayCoordinate) { } mitk::DisplayCoordinateOperation::DisplayCoordinateOperation(mitk::OperationType operationType, mitk::BaseRenderer *renderer, const mitk::Point2D &startDisplayCoordinate, const mitk::Point2D &lastDisplayCoordinate, const mitk::Point2D ¤tDisplayCoordinate, const mitk::Point2D &startCoordinateInMM) : mitk::Operation(operationType), m_Renderer(renderer), m_StartDisplayCoordinate(startDisplayCoordinate), m_LastDisplayCoordinate(lastDisplayCoordinate), m_CurrentDisplayCoordinate(currentDisplayCoordinate), m_StartCoordinateInMM(startCoordinateInMM) { } mitk::DisplayCoordinateOperation::~DisplayCoordinateOperation() { } mitk::BaseRenderer *mitk::DisplayCoordinateOperation::GetRenderer() { - return m_Renderer; + return m_Renderer.Lock(); } mitk::Vector2D mitk::DisplayCoordinateOperation::GetLastToCurrentDisplayVector() { return m_CurrentDisplayCoordinate - m_LastDisplayCoordinate; } mitk::Vector2D mitk::DisplayCoordinateOperation::GetStartToCurrentDisplayVector() { return m_CurrentDisplayCoordinate - m_StartDisplayCoordinate; } mitk::Vector2D mitk::DisplayCoordinateOperation::GetStartToLastDisplayVector() { return m_LastDisplayCoordinate - m_StartDisplayCoordinate; } diff --git a/Modules/Core/src/Rendering/mitkPlaneGeometryDataVtkMapper3D.cpp b/Modules/Core/src/Rendering/mitkPlaneGeometryDataVtkMapper3D.cpp index d3c4e18a87..5867760e97 100644 --- a/Modules/Core/src/Rendering/mitkPlaneGeometryDataVtkMapper3D.cpp +++ b/Modules/Core/src/Rendering/mitkPlaneGeometryDataVtkMapper3D.cpp @@ -1,589 +1,589 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "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) { SetVtkMapperImmediateModeRendering(m_EdgeMapper); SetVtkMapperImmediateModeRendering(m_BackgroundMapper); // 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::Pointer input = const_cast(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 // 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.IsNotNull()) + else if (!m_DataStorage.IsExpired()) { - m_SurfaceCreator->SetBoundingBox(m_DataStorage->ComputeVisibleBoundingBox(nullptr, "includeInBoundingBox")); + m_SurfaceCreator->SetBoundingBox(m_DataStorage.Lock()->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->GetSubset(predicateAllImages); + 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->GetLocalStorage(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(); // Enable rendering without copying the image. dataSetMapper->ImmediateModeRenderingOn(); 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->MapColorScalarsThroughLookupTableOff(); // re-use properties from the 2D image mapper imageActor->SetProperty(localStorage->m_Actor->GetProperty()); imageActor->GetProperty()->SetAmbient(0.5); // 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/Core/test/mitkWeakPointerTest.cpp b/Modules/Core/test/mitkWeakPointerTest.cpp index ee3e535ecb..19696a8ab5 100644 --- a/Modules/Core/test/mitkWeakPointerTest.cpp +++ b/Modules/Core/test/mitkWeakPointerTest.cpp @@ -1,51 +1,51 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkTestingMacros.h" #include #include int mitkWeakPointerTest(int /*argc*/, char * /*argv*/ []) { MITK_TEST_BEGIN("WeakPointer") mitk::WeakPointer weakPointer; mitk::WeakPointer weakPointer2; // Testing constructors and reference counting itk::Object::Pointer smartPointer = itk::Object::New(); mitk::WeakPointer weakPointer3(smartPointer); mitk::WeakPointer weakPointer4(weakPointer); { - itk::Object::Pointer tmpSmartPointer(weakPointer); - itk::Object::Pointer tmpSmartPointer2(weakPointer2); + itk::Object::Pointer tmpSmartPointer(weakPointer.Lock()); + itk::Object::Pointer tmpSmartPointer2(weakPointer2.Lock()); MITK_TEST_CONDITION_REQUIRED(tmpSmartPointer.GetPointer() == tmpSmartPointer2.GetPointer(), "Testing equal pointers"); } weakPointer = smartPointer; weakPointer2 = weakPointer; MITK_TEST_CONDITION_REQUIRED(1 == smartPointer->GetReferenceCount(), "Testing reference count"); smartPointer = nullptr; - MITK_TEST_CONDITION_REQUIRED(weakPointer.IsNull(), "Testing expired weak pointer (smart pointer assignment)"); - MITK_TEST_CONDITION_REQUIRED(weakPointer2.IsNull(), "Testing expired weak pointer (weak pointer assignment)"); - MITK_TEST_CONDITION_REQUIRED(weakPointer3.IsNull(), "Testing expired weak pointer (smart pointer constructor)"); - MITK_TEST_CONDITION_REQUIRED(weakPointer4.IsNull(), "Testing expired weak pointer (copy constructor)") + MITK_TEST_CONDITION_REQUIRED(weakPointer.IsExpired(), "Testing expired weak pointer (smart pointer assignment)"); + MITK_TEST_CONDITION_REQUIRED(weakPointer2.IsExpired(), "Testing expired weak pointer (weak pointer assignment)"); + MITK_TEST_CONDITION_REQUIRED(weakPointer3.IsExpired(), "Testing expired weak pointer (smart pointer constructor)"); + MITK_TEST_CONDITION_REQUIRED(weakPointer4.IsExpired(), "Testing expired weak pointer (copy constructor)") MITK_TEST_END() } diff --git a/Modules/DICOMReader/src/mitkDICOMImageBlockDescriptor.cpp b/Modules/DICOMReader/src/mitkDICOMImageBlockDescriptor.cpp index a777d79ce9..4ddcf30c79 100644 --- a/Modules/DICOMReader/src/mitkDICOMImageBlockDescriptor.cpp +++ b/Modules/DICOMReader/src/mitkDICOMImageBlockDescriptor.cpp @@ -1,863 +1,865 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkDICOMImageBlockDescriptor.h" #include "mitkStringProperty.h" #include "mitkLevelWindowProperty.h" #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(); } m_PropertyFunctor = &mitk::DICOMImageBlockDescriptor::GetPropertyForDICOMValues; } 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.IsNull() ) + 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.IsNull() ) + 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.IsNull() ) + if ( m_ImageFrameList.empty() || m_TagCache.IsExpired() ) { MITK_ERROR << "Invalid call to GetPixelSpacing. Need to have initialized tag-cache!"; return std::string( "" ); } static const DICOMTag tagPixelSpacing( 0x0028, 0x0030 ); - return m_TagCache->GetTagValue( m_ImageFrameList.front(), tagPixelSpacing ).value; + return m_TagCache.Lock()->GetTagValue( m_ImageFrameList.front(), tagPixelSpacing ).value; } std::string mitk::DICOMImageBlockDescriptor::GetImagerPixelSpacing() const { - if ( m_ImageFrameList.empty() || m_TagCache.IsNull() ) + if ( m_ImageFrameList.empty() || m_TagCache.IsExpired() ) { MITK_ERROR << "Invalid call to GetImagerPixelSpacing. Need to have initialized tag-cache!"; return std::string( "" ); } static const DICOMTag tagImagerPixelSpacing( 0x0018, 0x1164 ); - return m_TagCache->GetTagValue( m_ImageFrameList.front(), tagImagerPixelSpacing ).value; + return m_TagCache.Lock()->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; // 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" ) ); // 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" ) ); // third part: get all found additional tags of interest for (auto tag : m_FoundAdditionalTags) { BaseProperty* prop = this->GetProperty(tag); if (prop) { mitkImage->SetProperty(tag.c_str(), prop); } } // 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.IsNotNull() ) + if ( !m_ImageFrameList.empty() && !m_TagCache.IsExpired() ) { static const DICOMTag tagSOPClassUID( 0x0008, 0x0016 ); - return m_TagCache->GetTagValue( m_ImageFrameList.front(), tagSOPClassUID ).value; + return m_TagCache.Lock()->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.IsNotNull() ) + 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 = m_TagCache->GetTagValue( firstFrame, t ).value; \ + 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 = m_TagCache->GetTagValue( firstFrame, t ).value; \ - const std::string tagValueLast = m_TagCache->GetTagValue( lastFrame, t ).value; \ + 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.IsNull() ) + if ( m_TagCache.IsExpired() ) { MITK_ERROR << "Invalid call to DICOMImageBlockDescriptor::UpdateImageDescribingProperties(). Need to " "have initialized tag-cache!"; return; } + auto tagCache = m_TagCache.Lock(); + 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; 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 = m_TagCache->GetTagValue( *frameIter, tagSliceLocation ).value; + const std::string sliceLocation = tagCache->GetTagValue( *frameIter, tagSliceLocation ).value; sliceLocationForSlices.SetTableValue( slice, sliceLocation ); - const std::string instanceNumber = m_TagCache->GetTagValue( *frameIter, tagInstanceNumber ).value; + const std::string instanceNumber = tagCache->GetTagValue( *frameIter, tagInstanceNumber ).value; instanceNumberForSlices.SetTableValue( slice, instanceNumber ); - const std::string sopInstanceUID = m_TagCache->GetTagValue( *frameIter, tagSOPInstanceNumber ).value; + const std::string sopInstanceUID = tagCache->GetTagValue( *frameIter, tagSOPInstanceNumber ).value; SOPInstanceUIDForSlices.SetTableValue( slice, sopInstanceUID ); const std::string filename = ( *frameIter )->Filename; filenamesForSlices.SetTableValue( slice, 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 = m_TagCache->GetTagValue( *frameIter, tag.first ); + 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", StringLookupTableProperty::New( 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 (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; } } diff --git a/Modules/OpenCVVideoSupport/mitkImageToOpenCVImageFilter.cpp b/Modules/OpenCVVideoSupport/mitkImageToOpenCVImageFilter.cpp index 72f8166ca1..7204a4fa73 100644 --- a/Modules/OpenCVVideoSupport/mitkImageToOpenCVImageFilter.cpp +++ b/Modules/OpenCVVideoSupport/mitkImageToOpenCVImageFilter.cpp @@ -1,111 +1,112 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkImageToOpenCVImageFilter.h" #include #include #include namespace mitk{ ImageToOpenCVImageFilter::ImageToOpenCVImageFilter() : m_OpenCVImage(nullptr) { m_sliceSelector = ImageSliceSelector::New(); } ImageToOpenCVImageFilter::~ImageToOpenCVImageFilter() { m_OpenCVImage = nullptr; } void ImageToOpenCVImageFilter::SetImage( Image* _Image ) { m_Image = _Image; } bool ImageToOpenCVImageFilter::CheckImage( Image* image ) { if(image == nullptr) { MITK_WARN << "MITK Image is 0"; return false; } if(image->GetDimension() > 2 ) { MITK_WARN << "Only 2D Images allowed"; return false; } return true; } IplImage* ImageToOpenCVImageFilter::GetOpenCVImage() { + auto image = m_Image.Lock(); - if(!this->CheckImage( m_Image )) + if(!this->CheckImage(image)) return nullptr; m_OpenCVImage = (nullptr); try { - AccessFixedTypeByItk(m_Image.GetPointer(), ItkImageProcessing, + AccessFixedTypeByItk(image.GetPointer(), ItkImageProcessing, MITK_ACCESSBYITK_PIXEL_TYPES_SEQ // gray image (UCRGBPixelType)(USRGBPixelType)(FloatRGBPixelType)(DoubleRGBPixelType), // rgb image (2) // dimensions ) } catch (const AccessByItkException& e) { std::cout << "Caught exception [from AccessFixedTypeByItk]: \n" << e.what() << "\n"; return nullptr; } return m_OpenCVImage; } cv::Mat ImageToOpenCVImageFilter::GetOpenCVMat() { IplImage* img = this->GetOpenCVImage(); cv::Mat mat; if( img ) { // do not copy data, then release just the header mat = cv::Mat ( img, false ); cvReleaseImageHeader( &img ); } return mat; } template void ImageToOpenCVImageFilter::ItkImageProcessing( itk::Image* image ) { m_OpenCVImage = itk::OpenCVImageBridge::ITKImageToIplImage(image); } void ImageToOpenCVImageFilter::SetInputFromTimeSlice(Image::Pointer mitkImage, int timeStep, int slice) { m_sliceSelector->SetInput(mitkImage); m_sliceSelector->SetSliceNr(slice); m_sliceSelector->SetTimeNr(timeStep); m_sliceSelector->Update(); this->SetImage(m_sliceSelector->GetOutput()); } } // end namespace mitk \ No newline at end of file diff --git a/Modules/QtWidgets/src/QmitkDataStorageComboBox.cpp b/Modules/QtWidgets/src/QmitkDataStorageComboBox.cpp index 7c3b722013..9a498c94df 100644 --- a/Modules/QtWidgets/src/QmitkDataStorageComboBox.cpp +++ b/Modules/QtWidgets/src/QmitkDataStorageComboBox.cpp @@ -1,428 +1,435 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "QmitkDataStorageComboBox.h" #include //#CTORS/DTOR 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() { // if there was an old storage, remove listeners - if (m_DataStorage.IsNotNull()) + if (!m_DataStorage.IsExpired()) { - this->m_DataStorage->AddNodeEvent.RemoveListener( + auto dataStorage = m_DataStorage.Lock(); + + dataStorage->AddNodeEvent.RemoveListener( mitk::MessageDelegate1(this, &QmitkDataStorageComboBox::AddNode)); - this->m_DataStorage->RemoveNodeEvent.RemoveListener( + dataStorage->RemoveNodeEvent.RemoveListener( mitk::MessageDelegate1(this, &QmitkDataStorageComboBox::RemoveNode)); } // we have lots of observers to nodes and their name properties, this get's ugly if nodes live longer than the box while (m_Nodes.size() > 0) RemoveNode(0); } //#PUBLIC GETTER mitk::DataStorage::Pointer QmitkDataStorageComboBox::GetDataStorage() const { - return m_DataStorage.GetPointer(); + 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; } //#PUBLIC SETTER void QmitkDataStorageComboBox::SetDataStorage(mitk::DataStorage *_DataStorage) { + auto dataStorage = m_DataStorage.Lock(); + // reset only if datastorage really changed - if (m_DataStorage.GetPointer() != _DataStorage) + if (dataStorage.GetPointer() != _DataStorage) { // if there was an old storage, remove listeners - if (m_DataStorage.IsNotNull()) + if (dataStorage.IsNotNull()) { - this->m_DataStorage->AddNodeEvent.RemoveListener( + dataStorage->AddNodeEvent.RemoveListener( mitk::MessageDelegate1(this, &QmitkDataStorageComboBox::AddNode)); - this->m_DataStorage->RemoveNodeEvent.RemoveListener( + dataStorage->RemoveNodeEvent.RemoveListener( mitk::MessageDelegate1( this, &QmitkDataStorageComboBox::RemoveNode)); } // set new storage m_DataStorage = _DataStorage; // if there is a new storage, add listeners - if (m_DataStorage.IsNotNull()) + if (!m_DataStorage.IsExpired()) { - this->m_DataStorage->AddNodeEvent.AddListener( + dataStorage = m_DataStorage.Lock(); + + dataStorage->AddNodeEvent.AddListener( mitk::MessageDelegate1(this, &QmitkDataStorageComboBox::AddNode)); - this->m_DataStorage->RemoveNodeEvent.AddListener( + dataStorage->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 didnt call ourself if (!m_BlockEvents) { m_BlockEvents = true; // pass a -1 to the InsertNode function in order to append the datatreenode to the end this->InsertNode(-1, _DataNode); m_BlockEvents = false; } } void QmitkDataStorageComboBox::RemoveNode(int index) { if (this->HasIndex(index)) { //# remove itk::Event observer mitk::DataNode *_DataNode = m_Nodes.at(index); // get name property first mitk::BaseProperty *nameProperty = _DataNode->GetProperty("name"); // if prop exists remove modified listener if (nameProperty) { nameProperty->RemoveObserver(m_NodesModifiedObserverTags[index]); // remove name property map m_PropertyToNode.erase(_DataNode); } // then remove delete listener on the node itself _DataNode->RemoveObserver(m_NodesDeleteObserverTags[index]); // remove observer tags from lists m_NodesModifiedObserverTags.erase(m_NodesModifiedObserverTags.begin() + index); m_NodesDeleteObserverTags.erase(m_NodesDeleteObserverTags.begin() + index); // remove node from node vector m_Nodes.erase(m_Nodes.begin() + 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 didnt 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)) { 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::OnDataNodeDeleteOrModified(const itk::Object *caller, const itk::EventObject &event) { if (!m_BlockEvents) { m_BlockEvents = true; // check if we have a modified event (if not it is a delete event) const itk::ModifiedEvent *modifiedEvent = dynamic_cast(&event); // when node was modified reset text if (modifiedEvent) { const mitk::BaseProperty *_NameProperty = dynamic_cast(caller); // node name changed, set it // but first of all find associated node for (auto it = m_PropertyToNode.begin(); it != m_PropertyToNode.end(); ++it) { // property is found take node if (it->second == _NameProperty) { // looks strange but when calling setnode with the same node, that means the node gets updated this->SetNode(it->first, it->first); break; } } } else { const mitk::DataNode *_ConstDataNode = dynamic_cast(caller); if (_ConstDataNode) // node will be deleted, remove it this->RemoveNode(_ConstDataNode); } m_BlockEvents = false; } } void QmitkDataStorageComboBox::SetSelectedNode(mitk::DataNode::Pointer item) { int index = this->Find(item); if (index == -1) { MITK_INFO << "QmitkDataStorageComboBox: item not available"; } else { this->setCurrentIndex(index); } } //#PROTECTED GETTER bool QmitkDataStorageComboBox::HasIndex(unsigned int index) const { return (m_Nodes.size() > 0 && index < m_Nodes.size()); } int QmitkDataStorageComboBox::Find(const mitk::DataNode *_DataNode) const { int 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 index; } //#PROTECTED SETTER void QmitkDataStorageComboBox::OnCurrentIndexChanged(int index) { if (index >= 0 && index < this->count()) emit OnSelectionChanged(this->GetSelectedNode()); if (index == -1) emit OnSelectionChanged(nullptr); } 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); mitk::BaseProperty *nameProperty = _NonConstDataNode->GetProperty("name"); if (!changedNode) { // break on duplicated nodes (that doesnt make sense to have duplicates in the combobox) if (this->Find(_DataNode) != -1) return; // add modified observer itk::MemberCommand::Pointer modifiedCommand = itk::MemberCommand::New(); modifiedCommand->SetCallbackFunction(this, &QmitkDataStorageComboBox::OnDataNodeDeleteOrModified); // !!!! add modified observer for the name /// property of the node because this is the only thing we are interested in !!!!! if (nameProperty) { m_NodesModifiedObserverTags.push_back(nameProperty->AddObserver(itk::ModifiedEvent(), modifiedCommand)); m_PropertyToNode[_NonConstDataNode] = nameProperty; } // if there is no name node save an invalid value for the observer tag (-1) else m_NodesModifiedObserverTags.push_back(-1); // add delete observer itk::MemberCommand::Pointer deleteCommand = itk::MemberCommand::New(); deleteCommand->SetCallbackFunction(this, &QmitkDataStorageComboBox::OnDataNodeDeleteOrModified); m_NodesDeleteObserverTags.push_back(_NonConstDataNode->AddObserver(itk::DeleteEvent(), modifiedCommand)); } // add node to the vector if (addNewNode) m_Nodes.push_back(_NonConstDataNode); else if (insertNewNode) m_Nodes.insert(m_Nodes.begin() + index, _NonConstDataNode); // ... and to the combobox std::string _NonConstDataNodeName = "unnamed node"; // _NonConstDataNodeName is "unnamed node" so far, change it if there is a name property in the node if (nameProperty) _NonConstDataNodeName = nameProperty->GetValueAsString(); if (addNewNode) { this->addItem(QString::fromStdString(_NonConstDataNodeName)); // 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(_NonConstDataNodeName)); } } 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 // explicietely 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.IsNotNull()) + if (!m_DataStorage.IsExpired()) { + auto dataStorage = m_DataStorage.Lock(); mitk::DataStorage::SetOfObjects::ConstPointer setOfObjects; // select all if predicate == nullptr if (m_Predicate.IsNotNull()) - setOfObjects = m_DataStorage->GetSubset(m_Predicate); + setOfObjects = dataStorage->GetSubset(m_Predicate); else - setOfObjects = m_DataStorage->GetAll(); + 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()); } } } diff --git a/Modules/QtWidgets/src/QmitkDataStorageTableModel.cpp b/Modules/QtWidgets/src/QmitkDataStorageTableModel.cpp index b2ae7fd2b4..f555b5fbee 100644 --- a/Modules/QtWidgets/src/QmitkDataStorageTableModel.cpp +++ b/Modules/QtWidgets/src/QmitkDataStorageTableModel.cpp @@ -1,514 +1,520 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "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.GetPointer(); + 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.GetPointer() != _DataStorage.GetPointer()) + if (m_DataStorage != _DataStorage) { // if a data storage was set before remove old event listeners - if (m_DataStorage.IsNotNull()) + if (!m_DataStorage.IsExpired()) { - this->m_DataStorage->AddNodeEvent.RemoveListener( + auto dataStorage = m_DataStorage.Lock(); + + dataStorage->AddNodeEvent.RemoveListener( mitk::MessageDelegate1( this, &QmitkDataStorageTableModel::AddNode)); - this->m_DataStorage->RemoveNodeEvent.RemoveListener( + dataStorage->RemoveNodeEvent.RemoveListener( mitk::MessageDelegate1( this, &QmitkDataStorageTableModel::RemoveNode)); } // set new data storage - m_DataStorage = _DataStorage.GetPointer(); + m_DataStorage = _DataStorage; // if new storage is not 0 subscribe for events - if (m_DataStorage.IsNotNull()) + if (!m_DataStorage.IsExpired()) { + auto dataStorage = m_DataStorage.Lock(); + // subscribe for node added/removed events - this->m_DataStorage->AddNodeEvent.AddListener( + dataStorage->AddNodeEvent.AddListener( mitk::MessageDelegate1( this, &QmitkDataStorageTableModel::AddNode)); - this->m_DataStorage->RemoveNodeEvent.AddListener( + 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(); // the whole reset depends on the fact if a data storage is set or not - if (m_DataStorage.IsNotNull()) + if (!m_DataStorage.IsExpired()) { + auto dataStorage = m_DataStorage.Lock(); + if (m_Predicate.IsNotNull()) // get subset - _NodeSet = m_DataStorage->GetSubset(m_Predicate); + _NodeSet = dataStorage->GetSubset(m_Predicate); // if predicate is nullptr, select all nodes else { - _NodeSet = m_DataStorage->GetAll(); + _NodeSet = dataStorage->GetAll(); // remove ghost root node } // 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 463c57c6bf..b99c4a9d2b 100644 --- a/Modules/QtWidgets/src/QmitkDataStorageTreeModel.cpp +++ b/Modules/QtWidgets/src/QmitkDataStorageTreeModel.cpp @@ -1,1035 +1,1041 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include #include #include #include #include #include #include #include #include #include #include #include "QmitkDataStorageTreeModel.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(0), m_PlaceNewNodesOnTop(_PlaceNewNodesOnTop), m_Root(0), m_BlockDataStorageEvents(false), m_AllowHierarchyChange(false) { this->SetDataStorage(_DataStorage); } QmitkDataStorageTreeModel::~QmitkDataStorageTreeModel() { // set data storage to 0 = remove all listeners this->SetDataStorage(0); m_Root->Delete(); m_Root = 0; } mitk::DataNode::Pointer QmitkDataStorageTreeModel::GetNode(const QModelIndex &index) const { return this->TreeItemFromIndex(index)->GetDataNode(); } const mitk::DataStorage::Pointer QmitkDataStorageTreeModel::GetDataStorage() const { - return m_DataStorage.GetPointer(); + 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 { mitk::DataNode *dataNode = this->TreeItemFromIndex(index)->GetDataNode(); if (index.isValid()) { if (DicomPropertiesExists(*dataNode)) { return Qt::ItemIsUserCheckable | Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled; } 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 *droppedNode = (*diIter)->GetDataNode(); mitk::DataNode *dropOntoNode = dropItem->GetDataNode(); - m_DataStorage->Remove(droppedNode); - m_DataStorage->Add(droppedNode, dropOntoNode); + 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) { - if (node && m_DataStorage.IsNotNull() && !m_DataStorage->Exists(node)) + if (node && !m_DataStorage.IsExpired() && !m_DataStorage.Lock()->Exists(node)) { - m_DataStorage->Add(node); + m_DataStorage.Lock()->Add(node); mitk::BaseData::Pointer basedata = node->GetData(); if (basedata.IsNotNull()) { mitk::RenderingManager::GetInstance()->InitializeViews( basedata->GetTimeGeometry(), mitk::RenderingManager::REQUEST_UPDATE_ALL, true); 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; if (DicomPropertiesExists(*dataNode)) { mitk::BaseProperty *seriesDescription = (dataNode->GetProperty(mitk::GeneratePropertyNameForDICOMTag(0x0008, 0x103e).c_str())); mitk::BaseProperty *studyDescription = (dataNode->GetProperty(mitk::GeneratePropertyNameForDICOMTag(0x0008, 0x1030).c_str())); mitk::BaseProperty *patientsName = (dataNode->GetProperty(mitk::GeneratePropertyNameForDICOMTag(0x0010, 0x0010).c_str())); mitk::BaseProperty *seriesDescription_deprecated = (dataNode->GetProperty("dicom.series.SeriesDescription")); mitk::BaseProperty *studyDescription_deprecated = (dataNode->GetProperty("dicom.study.StudyDescription")); mitk::BaseProperty *patientsName_deprecated = (dataNode->GetProperty("dicom.patient.PatientsName")); if (patientsName) { nodeName += QString::fromStdString(patientsName->GetValueAsString()) + "\n"; nodeName += QString::fromStdString(studyDescription->GetValueAsString()) + "\n"; nodeName += QString::fromStdString(seriesDescription->GetValueAsString()); } else { /** Code coveres the deprecated property naming for backwards compatibility */ nodeName += QString::fromStdString(patientsName_deprecated->GetValueAsString()) + "\n"; nodeName += QString::fromStdString(studyDescription_deprecated->GetValueAsString()) + "\n"; nodeName += QString::fromStdString(seriesDescription_deprecated->GetValueAsString()); } } else { 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(0); } 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 { - if (m_DataStorage.IsNotNull()) + if (!m_DataStorage.IsExpired()) { + auto dataStorage = m_DataStorage.Lock(); + // remove Listener for the data storage itself m_DataStorage.ObjectDelete.RemoveListener(mitk::MessageDelegate1( this, &QmitkDataStorageTreeModel::SetDataStorageDeleted)); // remove listeners for the nodes - m_DataStorage->AddNodeEvent.RemoveListener( + dataStorage->AddNodeEvent.RemoveListener( mitk::MessageDelegate1(this, &QmitkDataStorageTreeModel::AddNode)); - m_DataStorage->ChangedNodeEvent.RemoveListener( + dataStorage->ChangedNodeEvent.RemoveListener( mitk::MessageDelegate1( this, &QmitkDataStorageTreeModel::SetNodeModified)); - m_DataStorage->RemoveNodeEvent.RemoveListener( + dataStorage->RemoveNodeEvent.RemoveListener( mitk::MessageDelegate1( this, &QmitkDataStorageTreeModel::RemoveNode)); } // 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, 0); this->beginResetModel(); this->endResetModel(); - if (m_DataStorage.IsNotNull()) + if (!m_DataStorage.IsExpired()) { + auto dataStorage = m_DataStorage.Lock(); + // add Listener for the data storage itself m_DataStorage.ObjectDelete.AddListener(mitk::MessageDelegate1( this, &QmitkDataStorageTreeModel::SetDataStorageDeleted)); // add listeners for the nodes - m_DataStorage->AddNodeEvent.AddListener(mitk::MessageDelegate1( + dataStorage->AddNodeEvent.AddListener(mitk::MessageDelegate1( this, &QmitkDataStorageTreeModel::AddNode)); - m_DataStorage->ChangedNodeEvent.AddListener( + dataStorage->ChangedNodeEvent.AddListener( mitk::MessageDelegate1( this, &QmitkDataStorageTreeModel::SetNodeModified)); - m_DataStorage->RemoveNodeEvent.AddListener( + dataStorage->RemoveNodeEvent.AddListener( mitk::MessageDelegate1( this, &QmitkDataStorageTreeModel::RemoveNode)); - mitk::DataStorage::SetOfObjects::ConstPointer _NodeSet = m_DataStorage->GetSubset(m_Predicate); + mitk::DataStorage::SetOfObjects::ConstPointer _NodeSet = dataStorage->GetSubset(m_Predicate); // finally add all nodes to the model this->Update(); } } } void QmitkDataStorageTreeModel::SetDataStorageDeleted(const itk::Object * /*_DataStorage*/) { this->SetDataStorage(0); } void QmitkDataStorageTreeModel::AddNodeInternal(const mitk::DataNode *node) { - if (node == 0 || m_DataStorage.IsNull() || !m_DataStorage->Exists(node) || m_Root->Find(node) != 0) + if (node == 0 || m_DataStorage.IsExpired() || !m_DataStorage.Lock()->Exists(node) || m_Root->Find(node) != 0) 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 == 0 || m_BlockDataStorageEvents || m_DataStorage.IsNull() || !m_DataStorage->Exists(node) || + if (node == 0 || m_BlockDataStorageEvents || m_DataStorage.IsExpired() || !m_DataStorage.Lock()->Exists(node) || m_Root->Find(node) != 0) 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 == 0 || 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 = 0; - mitk::DataStorage::SetOfObjects::ConstPointer _Sources = m_DataStorage->GetSources(node); + 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); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } // 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; } QmitkDataStorageTreeModel::TreeItem::TreeItem(mitk::DataNode *_DataNode, TreeItem *_Parent) : m_Parent(_Parent), m_DataNode(_DataNode) { if (m_Parent) m_Parent->AddChild(this); } QmitkDataStorageTreeModel::TreeItem::~TreeItem() { if (m_Parent) m_Parent->RemoveChild(this); } void QmitkDataStorageTreeModel::TreeItem::Delete() { while (m_Children.size() > 0) delete m_Children.back(); delete this; } QmitkDataStorageTreeModel::TreeItem *QmitkDataStorageTreeModel::TreeItem::Find(const mitk::DataNode *_DataNode) const { QmitkDataStorageTreeModel::TreeItem *item = 0; if (_DataNode) { if (m_DataNode == _DataNode) item = const_cast(this); else { for (std::vector::const_iterator it = m_Children.begin(); it != m_Children.end(); ++it) { if (item) break; item = (*it)->Find(_DataNode); } } } return item; } int QmitkDataStorageTreeModel::TreeItem::IndexOfChild(const TreeItem *item) const { std::vector::const_iterator it = std::find(m_Children.begin(), m_Children.end(), item); return it != m_Children.end() ? std::distance(m_Children.begin(), it) : -1; } QmitkDataStorageTreeModel::TreeItem *QmitkDataStorageTreeModel::TreeItem::GetChild(int index) const { return (m_Children.size() > 0 && index >= 0 && index < (int)m_Children.size()) ? m_Children.at(index) : 0; } void QmitkDataStorageTreeModel::TreeItem::AddChild(TreeItem *item) { this->InsertChild(item); } void QmitkDataStorageTreeModel::TreeItem::RemoveChild(TreeItem *item) { std::vector::iterator it = std::find(m_Children.begin(), m_Children.end(), item); if (it != m_Children.end()) { m_Children.erase(it); item->SetParent(0); } } int QmitkDataStorageTreeModel::TreeItem::GetChildCount() const { return m_Children.size(); } int QmitkDataStorageTreeModel::TreeItem::GetIndex() const { if (m_Parent) return m_Parent->IndexOfChild(this); return 0; } QmitkDataStorageTreeModel::TreeItem *QmitkDataStorageTreeModel::TreeItem::GetParent() const { return m_Parent; } mitk::DataNode::Pointer QmitkDataStorageTreeModel::TreeItem::GetDataNode() const { return m_DataNode; } void QmitkDataStorageTreeModel::TreeItem::InsertChild(TreeItem *item, int index) { std::vector::iterator it = std::find(m_Children.begin(), m_Children.end(), item); if (it == m_Children.end()) { if (m_Children.size() > 0 && index >= 0 && index < (int)m_Children.size()) { it = m_Children.begin(); std::advance(it, index); m_Children.insert(it, item); } else m_Children.push_back(item); // add parent if necessary if (item->GetParent() != this) item->SetParent(this); } } std::vector QmitkDataStorageTreeModel::TreeItem::GetChildren() const { return m_Children; } void QmitkDataStorageTreeModel::TreeItem::SetParent(TreeItem *_Parent) { m_Parent = _Parent; if (m_Parent) m_Parent->AddChild(this); } void QmitkDataStorageTreeModel::Update() { - if (m_DataStorage.IsNotNull()) + if (!m_DataStorage.IsExpired()) { - mitk::DataStorage::SetOfObjects::ConstPointer _NodeSet = m_DataStorage->GetAll(); + mitk::DataStorage::SetOfObjects::ConstPointer _NodeSet = m_DataStorage.Lock()->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 c79d66414c..04abdb9ffe 100644 --- a/Modules/QtWidgets/src/QmitkPropertiesTableModel.cpp +++ b/Modules/QtWidgets/src/QmitkPropertiesTableModel.cpp @@ -1,530 +1,534 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "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.GetPointer(); + 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.GetPointer() != _PropertyList) + if (m_PropertyList != _PropertyList) { // Remove delete listener if there was a propertylist before - if (m_PropertyList.IsNotNull()) + if (!m_PropertyList.IsExpired()) { m_PropertyList.ObjectDelete.RemoveListener(mitk::MessageDelegate1( this, &QmitkPropertiesTableModel::PropertyListDelete)); } // set new list m_PropertyList = _PropertyList; - if (m_PropertyList.IsNotNull()) + if (!m_PropertyList.IsExpired()) { m_PropertyList.ObjectDelete.AddListener(mitk::MessageDelegate1( this, &QmitkPropertiesTableModel::PropertyListDelete)); } this->Reset(); } } void QmitkPropertiesTableModel::PropertyListDelete(const itk::Object * /*_PropertyList*/) { 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); - m_PropertyList->InvokeEvent(itk::ModifiedEvent()); - m_PropertyList->Modified(); + 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); - m_PropertyList->InvokeEvent(itk::ModifiedEvent()); - m_PropertyList->Modified(); + propertyList->InvokeEvent(itk::ModifiedEvent()); + propertyList->Modified(); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } else if (mitk::StringProperty *stringProp = dynamic_cast(baseProp)) { stringProp->SetValue((value.value()).toStdString()); - m_PropertyList->InvokeEvent(itk::ModifiedEvent()); - m_PropertyList->Modified(); + 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); - m_PropertyList->InvokeEvent(itk::ModifiedEvent()); - m_PropertyList->Modified(); + 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); - m_PropertyList->InvokeEvent(itk::ModifiedEvent()); - m_PropertyList->Modified(); + 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); - m_PropertyList->InvokeEvent(itk::ModifiedEvent()); - m_PropertyList->Modified(); + 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.IsNotNull()) + if (!m_PropertyList.IsExpired()) { + auto propertyList = m_PropertyList.Lock(); + // first of all: collect all properties from the list - for (auto it = m_PropertyList->GetMap()->begin(); it != m_PropertyList->GetMap()->end(); it++) + 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 1d114ace30..8e1f599046 100644 --- a/Modules/QtWidgets/src/QmitkPropertyItemDelegate.cpp +++ b/Modules/QtWidgets/src/QmitkPropertyItemDelegate.cpp @@ -1,395 +1,395 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "QmitkPropertyItemDelegate.h" #include "QmitkPropertyItemModel.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include mitk::IPropertyExtensions *GetPropertyService() { us::ModuleContext *context = us::GetModuleContext(); us::ServiceReference serviceRef = context->GetServiceReference(); return serviceRef ? context->GetService(serviceRef) : nullptr; } 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::IPropertyExtensions *extensions = GetPropertyService(); std::string name = this->GetPropertyName(index); if (extensions != nullptr && !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::IPropertyExtensions *extensions = GetPropertyService(); std::string name = this->GetPropertyName(index); if (extensions != nullptr && !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.IsNotNull()) + if (!m_PropertyList.IsExpired()) { mitk::BaseProperty *property = reinterpret_cast(index.data(mitk::PropertyRole).value()); - const mitk::PropertyList::PropertyMap *propertyMap = m_PropertyList->GetMap(); + const mitk::PropertyList::PropertyMap *propertyMap = m_PropertyList.Lock()->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.GetPointer() != propertyList) + if (m_PropertyList != propertyList) m_PropertyList = propertyList; } diff --git a/Modules/QtWidgets/src/QmitkPropertyItemModel.cpp b/Modules/QtWidgets/src/QmitkPropertyItemModel.cpp index 8825572b07..54a6209434 100644 --- a/Modules/QtWidgets/src/QmitkPropertyItemModel.cpp +++ b/Modules/QtWidgets/src/QmitkPropertyItemModel.cpp @@ -1,539 +1,541 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "QmitkPropertyItemModel.h" #include "QmitkPropertyItem.h" #include #include #include #include #include #include #include #include #include #include #include template T *GetPropertyService() { us::ModuleContext *context = us::GetModuleContext(); us::ServiceReference serviceRef = context->GetServiceReference(); return serviceRef ? context->GetService(serviceRef) : nullptr; } static QColor MitkToQt(const mitk::Color &color) { return QColor(color.GetRed() * 255, color.GetGreen() * 255, color.GetBlue() * 255); } static mitk::BaseProperty *GetBaseProperty(const QVariant &data) { return data.isValid() ? reinterpret_cast(data.value()) : nullptr; } static 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_ShowAliases(false), m_FilterProperties(false), m_PropertyAliases(nullptr), m_PropertyFilters(nullptr) { 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(); - auto propertyMap = m_PropertyList->GetMap(); + auto propertyMap = m_PropertyList.Lock()->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 : 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.GetPointer(); + 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::OnPreferencesChanged() { bool updateAliases = m_ShowAliases != (m_PropertyAliases != nullptr); bool updateFilters = m_FilterProperties != (m_PropertyFilters != nullptr); bool resetPropertyList = false; if (updateAliases) { m_PropertyAliases = m_ShowAliases ? GetPropertyService() : nullptr; - resetPropertyList = m_PropertyList.IsNotNull(); + resetPropertyList = !m_PropertyList.IsExpired(); } if (updateFilters) { m_PropertyFilters = m_FilterProperties ? GetPropertyService() : nullptr; if (!resetPropertyList) - resetPropertyList = m_PropertyList.IsNotNull(); + resetPropertyList = !m_PropertyList.IsExpired(); } if (resetPropertyList) - this->SetNewPropertyList(m_PropertyList.GetPointer()); + this->SetNewPropertyList(m_PropertyList.Lock()); } void QmitkPropertyItemModel::OnPropertyDeleted(const itk::Object * /*property*/, const itk::EventObject &) { /*QModelIndex index = this->FindProperty(static_cast(property)); if (index != QModelIndex()) this->reset();*/ } void QmitkPropertyItemModel::OnPropertyListModified(const itk::Object *propertyList) { this->SetNewPropertyList(dynamic_cast(const_cast(propertyList))); } void QmitkPropertyItemModel::OnPropertyListDeleted(const itk::Object *) { 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())); } - m_PropertyList->InvokeEvent(itk::ModifiedEvent()); - m_PropertyList->Modified(); + auto propertyList = m_PropertyList.Lock(); + + propertyList->InvokeEvent(itk::ModifiedEvent()); + propertyList->Modified(); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); return true; } void QmitkPropertyItemModel::SetNewPropertyList(mitk::PropertyList *propertyList) { typedef mitk::PropertyList::PropertyMap PropertyMap; this->beginResetModel(); - if (m_PropertyList.IsNotNull()) + if (!m_PropertyList.IsExpired()) { mitk::MessageDelegate1 onPropertyListDeleted( this, &QmitkPropertyItemModel::OnPropertyListDeleted); m_PropertyList.ObjectDelete.RemoveListener(onPropertyListDeleted); mitk::MessageDelegate1 onPropertyListModified( this, &QmitkPropertyItemModel::OnPropertyListModified); m_PropertyList.ObjectModified.RemoveListener(onPropertyListModified); - const PropertyMap *propertyMap = m_PropertyList->GetMap(); + const PropertyMap *propertyMap = m_PropertyList.Lock()->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 = propertyList; - if (m_PropertyList.IsNotNull()) + if (!m_PropertyList.IsExpired()) { mitk::MessageDelegate1 onPropertyListModified( this, &QmitkPropertyItemModel::OnPropertyListModified); m_PropertyList.ObjectModified.AddListener(onPropertyListModified); mitk::MessageDelegate1 onPropertyListDeleted( this, &QmitkPropertyItemModel::OnPropertyListDeleted); m_PropertyList.ObjectDelete.AddListener(onPropertyListDeleted); mitk::MessageDelegate2 onPropertyModified( this, &QmitkPropertyItemModel::OnPropertyModified); itk::MemberCommand::Pointer modifiedCommand = itk::MemberCommand::New(); modifiedCommand->SetCallbackFunction(this, &QmitkPropertyItemModel::OnPropertyModified); - const PropertyMap *propertyMap = m_PropertyList->GetMap(); + 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))); itk::MemberCommand::Pointer deletedCommand = itk::MemberCommand::New(); deletedCommand->SetCallbackFunction(this, &QmitkPropertyItemModel::OnPropertyDeleted); for (PropertyMap::const_iterator it = propertyMap->begin(); it != propertyMap->end(); ++it) m_PropertyDeletedTags.insert( std::make_pair(it->first, it->second->AddObserver(itk::DeleteEvent(), deletedCommand))); } this->CreateRootItem(); - if (m_PropertyList != nullptr && !m_PropertyList->IsEmpty()) + if (m_PropertyList != nullptr && !m_PropertyList.Lock()->IsEmpty()) { mitk::PropertyList::PropertyMap filteredProperties; bool filterProperties = false; if (m_PropertyFilters != nullptr && (m_PropertyFilters->HasFilter() || m_PropertyFilters->HasFilter(m_ClassName.toStdString()))) { - filteredProperties = m_PropertyFilters->ApplyFilter(*m_PropertyList->GetMap(), m_ClassName.toStdString()); + filteredProperties = m_PropertyFilters->ApplyFilter(*m_PropertyList.Lock()->GetMap(), m_ClassName.toStdString()); filterProperties = true; } const mitk::PropertyList::PropertyMap *propertyMap = - !filterProperties ? m_PropertyList->GetMap() : &filteredProperties; + !filterProperties ? m_PropertyList.Lock()->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; if (m_PropertyAliases != nullptr) { 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.GetPointer() != propertyList) + if (m_PropertyList != propertyList) { m_ClassName = className; this->SetNewPropertyList(propertyList); } } void QmitkPropertyItemModel::Update() { - this->SetNewPropertyList(m_PropertyList); + this->SetNewPropertyList(m_PropertyList.Lock()); } diff --git a/Modules/QtWidgetsExt/src/QmitkPointListViewWidget.cpp b/Modules/QtWidgetsExt/src/QmitkPointListViewWidget.cpp index 1f98432b6e..acc9ab0fee 100644 --- a/Modules/QtWidgetsExt/src/QmitkPointListViewWidget.cpp +++ b/Modules/QtWidgetsExt/src/QmitkPointListViewWidget.cpp @@ -1,247 +1,255 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "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(0); // remove listener } void QmitkPointListViewWidget::SetPointSet(mitk::PointSet *pointSet) { - if (m_PointSet.IsNotNull()) + if (!m_PointSet.IsExpired()) { m_PointSet.ObjectModified.RemoveListener(mitk::MessageDelegate1( this, &QmitkPointListViewWidget::OnPointSetChanged)); m_PointSet.ObjectDelete.RemoveListener(mitk::MessageDelegate1( this, &QmitkPointListViewWidget::OnPointSetDeleted)); } m_PointSet = pointSet; - if (m_PointSet.IsNotNull()) + if (!m_PointSet.IsExpired()) { m_PointSet.ObjectModified.AddListener(mitk::MessageDelegate1( this, &QmitkPointListViewWidget::OnPointSetChanged)); m_PointSet.ObjectDelete.AddListener(mitk::MessageDelegate1( this, &QmitkPointListViewWidget::OnPointSetDeleted)); } this->Update(); } const mitk::PointSet *QmitkPointListViewWidget::GetPointSet() const { - return m_PointSet; + 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(const itk::Object *) { if (!m_SelfCall) this->Update(); } void QmitkPointListViewWidget::OnPointSetDeleted(const itk::Object *) { this->SetPointSet(0); this->Update(); } void QmitkPointListViewWidget::OnItemDoubleClicked(QListWidgetItem *item) { QmitkEditPointDialog _EditPointDialog(this); - _EditPointDialog.SetPoint(m_PointSet, this->row(item), m_TimeStep); + _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.IsNull()) + 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.IsNull()) + if (m_PointSet.IsExpired()) return; + auto pointSet = m_PointSet.Lock(); + mitk::PointSet::PointIdentifier selectedID; - selectedID = m_PointSet->SearchSelectedPoint(m_TimeStep); + selectedID = pointSet->SearchSelectedPoint(m_TimeStep); mitk::PointOperation *doOp = - new mitk::PointOperation(mitk::OpMOVEPOINTUP, m_PointSet->GetPoint(selectedID, m_TimeStep), selectedID, true); - m_PointSet->ExecuteOperation(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.IsNull()) + if (m_PointSet.IsExpired()) return; + auto pointSet = m_PointSet.Lock(); + mitk::PointSet::PointIdentifier selectedID; - selectedID = m_PointSet->SearchSelectedPoint(m_TimeStep); + selectedID = pointSet->SearchSelectedPoint(m_TimeStep); mitk::PointOperation *doOp = - new mitk::PointOperation(mitk::OpMOVEPOINTDOWN, m_PointSet->GetPoint(selectedID, m_TimeStep), selectedID, true); - m_PointSet->ExecuteOperation(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.IsNull()) + if (m_PointSet.IsExpired()) return; + auto pointSet = m_PointSet.Lock(); + mitk::PointSet::PointIdentifier selectedID; - selectedID = m_PointSet->SearchSelectedPoint(m_TimeStep); + selectedID = pointSet->SearchSelectedPoint(m_TimeStep); mitk::PointOperation *doOp = - new mitk::PointOperation(mitk::OpREMOVE, m_PointSet->GetPoint(selectedID, m_TimeStep), selectedID, true); - m_PointSet->ExecuteOperation(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.IsNull()) + if (m_PointSet.IsExpired()) { this->clear(); return; } + auto pointSet = m_PointSet.Lock(); + m_SelfCall = true; QString text; int i = 0; - mitk::PointSet::DataType::Pointer pointset = m_PointSet->GetPointSet(m_TimeStep); + 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()) - m_PointSet->SetSelectInfo(this->currentRow(), true, m_TimeStep); + pointSet->SetSelectInfo(this->currentRow(), true, m_TimeStep); else - m_PointSet->SetSelectInfo(it->Index(), false, m_TimeStep); // select nothing now + pointSet->SetSelectInfo(it->Index(), false, m_TimeStep); // select nothing now } ++i; } // remove unnecessary listwidgetitems - while (m_PointSet->GetPointSet(m_TimeStep)->GetPoints()->Size() < (unsigned int)this->count()) + 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 (m_PointSet->GetNumberOfSelected(m_TimeStep) > 1) + 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 = m_PointSet->SearchSelectedPoint(m_TimeStep); + int selectedIndex = pointSet->SearchSelectedPoint(m_TimeStep); if (selectedIndex != -1) // no selected point is found { this->setCurrentRow(selectedIndex); } } m_SelfCall = false; } diff --git a/Modules/Segmentation/Controllers/mitkToolManager.cpp b/Modules/Segmentation/Controllers/mitkToolManager.cpp index b632ed9e20..392ecb2d32 100644 --- a/Modules/Segmentation/Controllers/mitkToolManager.cpp +++ b/Modules/Segmentation/Controllers/mitkToolManager.cpp @@ -1,514 +1,514 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkToolManager.h" #include "mitkCoreObjectFactory.h" #include #include #include #include "mitkInteractionEventObserver.h" #include "mitkSegTool2D.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(); } 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); } } 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) { if (id != -1 && !this->GetToolById(id)->CanHandle(this->GetReferenceData(0)->GetData())) 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) { if (m_ActiveTool) { m_ActiveTool->Deactivated(); m_ActiveToolRegistration.Unregister(); } m_ActiveTool = GetToolById(nextTool); m_ActiveToolID = m_ActiveTool ? nextTool : -1; // current ID if tool is valid, otherwise -1 ActiveToolChanged.Send(); if (m_ActiveTool) { 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() { - if (m_DataStorage.IsNotNull()) + if (!m_DataStorage.IsExpired()) { - return m_DataStorage; + return m_DataStorage.Lock(); } else { return nullptr; } } 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()); } diff --git a/Plugins/org.mitk.gui.qt.imagecropper/src/internal/QmitkImageCropper.cpp b/Plugins/org.mitk.gui.qt.imagecropper/src/internal/QmitkImageCropper.cpp index c2d556beba..c46bb828bd 100644 --- a/Plugins/org.mitk.gui.qt.imagecropper/src/internal/QmitkImageCropper.cpp +++ b/Plugins/org.mitk.gui.qt.imagecropper/src/internal/QmitkImageCropper.cpp @@ -1,604 +1,606 @@ /*========================================================================= Program: Medical Imaging & Interaction Toolkit Language: C++ Date: $Date$ Version: $Revision$ Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. See MITKCopyright.txt or http://www.mitk.org/copyright.html for details. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the above copyright notices for more information. =========================================================================*/ #include "QmitkImageCropper.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include // Includes for image casting between ITK and MITK: added after using Plugin Generator #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include const std::string QmitkImageCropper::VIEW_ID = "org.mitk.views.qmitkimagecropper"; QmitkImageCropper::QmitkImageCropper(QObject *) : m_ParentWidget(0), m_ImageNode(nullptr), m_CroppingObject(nullptr), m_CroppingObjectNode(nullptr), m_BoundingShapeInteractor(nullptr), m_CropOutsideValue(0), m_Advanced(0), m_Active(0), m_ScrollEnabled(true) { CreateBoundingShapeInteractor(false); } QmitkImageCropper::~QmitkImageCropper() { //delete pointer objects m_CroppingObjectNode = nullptr; m_CroppingObject = nullptr; //disable interactor if (m_BoundingShapeInteractor != nullptr) { m_BoundingShapeInteractor->SetDataNode(nullptr); m_BoundingShapeInteractor->EnableInteraction(false); } } void QmitkImageCropper::SetFocus() { m_Controls.buttonCreateNewBoundingBox->setFocus(); } void QmitkImageCropper::CreateQtPartControl(QWidget *parent) { // create GUI widgets from the Qt Designer's .ui file m_Controls.setupUi(parent); m_Controls.boundingShapeSelector->SetDataStorage(this->GetDataStorage()); m_Controls.boundingShapeSelector->SetPredicate(mitk::NodePredicateAnd::New( mitk::TNodePredicateDataType::New(), mitk::NodePredicateNot::New(mitk::NodePredicateProperty::New("helper object")))); m_CroppingObjectNode = m_Controls.boundingShapeSelector->GetSelectedNode(); connect(m_Controls.buttonCropping, SIGNAL(clicked()), this, SLOT(DoCropping())); connect(m_Controls.buttonMasking, SIGNAL(clicked()), this, SLOT(DoMasking())); connect(m_Controls.boundingShapeSelector, SIGNAL(OnSelectionChanged(const mitk::DataNode*)), this, SLOT(OnDataSelectionChanged(const mitk::DataNode*))); connect(m_Controls.buttonCreateNewBoundingBox, SIGNAL(clicked()), this, SLOT(DoCreateNewBoundingObject())); connect(m_Controls.buttonAdvancedSettings, SIGNAL(clicked()), this, SLOT(OnAdvancedSettingsButtonToggled())); connect(m_Controls.spinBoxOutsidePixelValue, SIGNAL(valueChanged(int)), this, SLOT(OnSliderValueChanged(int))); setDefaultGUI(); m_Controls.labelWarningRotation->setVisible(false); m_BoundingObjectNames.clear(); m_Advanced = false; this->OnAdvancedSettingsButtonToggled(); m_ParentWidget = parent; } void QmitkImageCropper::OnDataSelectionChanged(const mitk::DataNode*) { m_Controls.boundingShapeSelector->setEnabled(true); m_CroppingObjectNode = m_Controls.boundingShapeSelector->GetSelectedNode(); if (m_CroppingObjectNode.IsNotNull() && dynamic_cast(this->m_CroppingObjectNode->GetData())) { m_Controls.buttonAdvancedSettings->setEnabled(true); m_Controls.labelWarningBB->setVisible(false); m_CroppingObject = dynamic_cast(m_CroppingObjectNode->GetData()); m_Advanced = true; mitk::RenderingManager::GetInstance()->InitializeViews(); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } else { setDefaultGUI(); m_CroppingObject = nullptr; m_BoundingShapeInteractor->EnableInteraction(false); m_BoundingShapeInteractor->SetDataNode(nullptr); m_Advanced = false; this->OnAdvancedSettingsButtonToggled(); } } void QmitkImageCropper::OnAdvancedSettingsButtonToggled() { m_Controls.groupImageSettings->setVisible(m_Advanced); m_Advanced = !m_Advanced; } void QmitkImageCropper::CreateBoundingShapeInteractor(bool rotationEnabled) { if (m_BoundingShapeInteractor.IsNull()) { m_BoundingShapeInteractor = mitk::BoundingShapeInteractor::New(); m_BoundingShapeInteractor->LoadStateMachine("BoundingShapeInteraction.xml", us::ModuleRegistry::GetModule("MitkBoundingShape")); m_BoundingShapeInteractor->SetEventConfig("BoundingShapeMouseConfig.xml", us::ModuleRegistry::GetModule("MitkBoundingShape")); } m_BoundingShapeInteractor->SetRotationEnabled(rotationEnabled); } mitk::Geometry3D::Pointer QmitkImageCropper::InitializeWithImageGeometry(mitk::BaseGeometry::Pointer geometry) { // convert a basegeometry into a Geometry3D (otherwise IO is not working properly) if (geometry == nullptr) mitkThrow() << "Geometry is not valid."; auto boundingGeometry = mitk::Geometry3D::New(); boundingGeometry->SetBounds(geometry->GetBounds()); boundingGeometry->SetImageGeometry(geometry->GetImageGeometry()); boundingGeometry->SetOrigin(geometry->GetOrigin()); boundingGeometry->SetSpacing(geometry->GetSpacing()); boundingGeometry->SetIndexToWorldTransform(geometry->GetIndexToWorldTransform()); boundingGeometry->Modified(); return boundingGeometry; } QString QmitkImageCropper::getAdaptedBoundingObjectName(const QString& name) const { bool nameNotTaken = false; unsigned int counter = 2; QString newName; while (!nameNotTaken) { newName = name + "_" + QString::number(counter); if (!m_BoundingObjectNames.contains(newName)) { nameNotTaken = true; } ++counter; } return newName; } void QmitkImageCropper::DoCreateNewBoundingObject() { - if (m_ImageNode.IsNotNull()) + if (!m_ImageNode.IsExpired()) { + auto imageNode = m_ImageNode.Lock(); + bool ok = false; QString defaultName = "BoundingShape"; if (m_BoundingObjectNames.contains(defaultName)) { defaultName = getAdaptedBoundingObjectName(defaultName); } QString name = QInputDialog::getText(QApplication::activeWindow() , "Add cropping shape...", "Enter name for the new cropping shape", QLineEdit::Normal, defaultName, &ok); if (!ok) return; if (name == "") { name = "Bounding Shape"; } if (m_BoundingObjectNames.contains(name)) { name = getAdaptedBoundingObjectName(name); QMessageBox::information(nullptr, "Information", "Bounding object name already exists. It was changed to: " + name); } m_BoundingObjectNames.append(name); m_Controls.buttonCropping->setEnabled(true); m_Controls.buttonMasking->setEnabled(true); m_Controls.boundingShapeSelector->setEnabled(true); m_Controls.groupImageSettings->setEnabled(true); // get current timestep to support 3d+t images auto renderWindowPart = this->GetRenderWindowPart(OPEN); int timeStep = renderWindowPart->GetTimeNavigationController()->GetTime()->GetPos(); - mitk::BaseGeometry::Pointer imageGeometry = static_cast(m_ImageNode->GetData()->GetGeometry(timeStep)); + mitk::BaseGeometry::Pointer imageGeometry = static_cast(imageNode->GetData()->GetGeometry(timeStep)); m_CroppingObject = mitk::GeometryData::New(); m_CroppingObject->SetGeometry(static_cast(this->InitializeWithImageGeometry(imageGeometry))); m_CroppingObjectNode = mitk::DataNode::New(); m_CroppingObjectNode->SetData(m_CroppingObject); m_CroppingObjectNode->SetProperty("name", mitk::StringProperty::New(name.toStdString())); m_CroppingObjectNode->SetProperty("color", mitk::ColorProperty::New(1.0, 1.0, 1.0)); m_CroppingObjectNode->SetProperty("opacity", mitk::FloatProperty::New(0.6)); m_CroppingObjectNode->SetProperty("layer", mitk::IntProperty::New(99)); m_CroppingObjectNode->AddProperty("handle size factor", mitk::DoubleProperty::New(1.0 / 40.0)); m_CroppingObjectNode->SetBoolProperty("pickable", true); if (!this->GetDataStorage()->Exists(m_CroppingObjectNode)) { - GetDataStorage()->Add(m_CroppingObjectNode, m_ImageNode); + GetDataStorage()->Add(m_CroppingObjectNode, imageNode); m_Controls.boundingShapeSelector->SetSelectedNode(m_CroppingObjectNode); m_CroppingObjectNode->SetVisibility(true); m_BoundingShapeInteractor->EnableInteraction(true); m_BoundingShapeInteractor->SetDataNode(this->m_CroppingObjectNode); this->OnDataSelectionChanged(m_CroppingObjectNode); } } // Adjust coordinate system by doing a reinit on auto tempDataStorage = mitk::DataStorage::SetOfObjects::New(); tempDataStorage->InsertElement(0, m_CroppingObjectNode); //// initialize the views to the bounding geometry //mitk::TimeGeometry::Pointer bounds = this->GetDataStorage()->ComputeBoundingGeometry3D(tempDataStorage); //mitk::RenderingManager::GetInstance()->InitializeViews(bounds); //mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } void QmitkImageCropper::setDefaultGUI() { m_Controls.labelWarningImage->setStyleSheet(" QLabel { color: rgb(255, 0, 0) }"); m_Controls.labelWarningImage->setText(QString::fromStdString("Select an image.")); m_Controls.labelWarningImage->setVisible(true); m_Controls.labelWarningBB->setStyleSheet(" QLabel { color: rgb(255, 0, 0) }"); m_Controls.labelWarningBB->setText(QString::fromStdString("Create a bounding shape below.")); m_Controls.labelWarningBB->setVisible(true); m_Controls.buttonCreateNewBoundingBox->setEnabled(false); m_Controls.labelWarningRotation->setVisible(false); m_Controls.buttonCropping->setEnabled(false); m_Controls.buttonMasking->setEnabled(false); m_Controls.boundingShapeSelector->setEnabled(false); m_Controls.buttonAdvancedSettings->setEnabled(false); m_Controls.groupImageSettings->setEnabled(false); m_Controls.checkOverwriteImage->setChecked(false); m_Controls.checkBoxCropTimeStepOnly->setChecked(false); } void QmitkImageCropper::OnSelectionChanged(berry::IWorkbenchPart::Pointer /*part*/, const QList& nodes) { bool rotationEnabled = false; if (nodes.empty()) { setDefaultGUI(); return; } m_ParentWidget->setEnabled(true); foreach(mitk::DataNode::Pointer node, nodes) { if (node.IsNotNull() && dynamic_cast(node->GetData())) { m_ImageNode = nodes[0]; m_Controls.groupBoundingObject->setEnabled(true); m_Controls.labelWarningImage->setStyleSheet(" QLabel { color: rgb(0, 0, 0) }"); - m_Controls.labelWarningImage->setText(QString::fromStdString("File name: " + m_ImageNode->GetName())); + m_Controls.labelWarningImage->setText(QString::fromStdString("File name: " + nodes[0]->GetName())); m_Controls.buttonCreateNewBoundingBox->setEnabled(true); - mitk::Image::Pointer image = dynamic_cast(m_ImageNode->GetData()); + mitk::Image::Pointer image = dynamic_cast(nodes[0]->GetData()); if (image != nullptr) { if (image->GetDimension() < 3) { QMessageBox::warning(nullptr, tr("Invalid image selected"), tr("ImageCropper only works with 3 or more dimensions."), QMessageBox::Ok, QMessageBox::NoButton, QMessageBox::NoButton); setDefaultGUI(); return; } vtkSmartPointer imageMat = image->GetGeometry()->GetVtkMatrix(); // check whether the image geometry is rotated, if so, no pixel aligned cropping or masking can be performed if ((imageMat->GetElement(1, 0) == 0.0) && (imageMat->GetElement(0, 1) == 0.0) && (imageMat->GetElement(1, 2) == 0.0) && (imageMat->GetElement(2, 1) == 0.0) && (imageMat->GetElement(2, 0) == 0.0) && (imageMat->GetElement(0, 2) == 0.0)) { rotationEnabled = false; m_Controls.labelWarningRotation->setVisible(false); } else { rotationEnabled = true; m_Controls.labelWarningRotation->setStyleSheet(" QLabel { color: rgb(255, 0, 0) }"); m_Controls.labelWarningRotation->setVisible(true); } this->CreateBoundingShapeInteractor(rotationEnabled); m_CroppingObjectNode = m_Controls.boundingShapeSelector->GetSelectedNode(); if (m_CroppingObjectNode != nullptr) { this->OnDataSelectionChanged(m_CroppingObjectNode); m_BoundingShapeInteractor->EnableInteraction(true); m_BoundingShapeInteractor->SetDataNode(this->m_CroppingObjectNode); m_Controls.boundingShapeSelector->setEnabled(true); } if (image->GetPixelType().GetPixelType() == itk::ImageIOBase::SCALAR) { // Might be changed with the upcoming new image statistics plugin //(recomputation might be very expensive for large images ;) ) auto statistics = image->GetStatistics(); auto minPixelValue = statistics->GetScalarValueMin(); auto maxPixelValue = statistics->GetScalarValueMax(); if (minPixelValue < std::numeric_limits::min()) { minPixelValue = std::numeric_limits::min(); } if (maxPixelValue > std::numeric_limits::max()) { maxPixelValue = std::numeric_limits::max(); } m_Controls.spinBoxOutsidePixelValue->setEnabled(true); m_Controls.spinBoxOutsidePixelValue->setMaximum(static_cast(maxPixelValue)); m_Controls.spinBoxOutsidePixelValue->setMinimum(static_cast(minPixelValue)); m_Controls.spinBoxOutsidePixelValue->setValue(static_cast(minPixelValue)); } else { m_Controls.spinBoxOutsidePixelValue->setEnabled(false); } unsigned int dim = image->GetDimension(); if (dim < 2 || dim > 4) { m_Controls.labelWarningImage->setStyleSheet(" QLabel { color: rgb(255, 0, 0) }"); m_Controls.labelWarningImage->setText(QString::fromStdString("Select an image.")); m_ParentWidget->setEnabled(false); } if (m_CroppingObjectNode != nullptr) { m_Controls.buttonCropping->setEnabled(true); m_Controls.buttonMasking->setEnabled(true); m_Controls.boundingShapeSelector->setEnabled(true); m_Controls.labelWarningBB->setVisible(false); } else { m_Controls.buttonCropping->setEnabled(false); m_Controls.buttonMasking->setEnabled(false); m_Controls.boundingShapeSelector->setEnabled(false); m_Controls.labelWarningBB->setVisible(true); } return; } // iterate all selected objects, adjust warning visibility setDefaultGUI(); m_ParentWidget->setEnabled(true); m_Controls.labelWarningRotation->setVisible(false); } } } void QmitkImageCropper::NodeRemoved(const mitk::DataNode * node) { QList::iterator it = m_BoundingObjectNames.begin(); while (it != m_BoundingObjectNames.end()) { if (node->GetName() == it->toStdString()) { it = m_BoundingObjectNames.erase(it); } else { ++it; } } } void QmitkImageCropper::OnComboBoxSelectionChanged(const mitk::DataNode* node) { mitk::DataNode* selectedNode = const_cast(node); if (selectedNode != nullptr) { - if (m_ImageNode.IsNotNull()) - selectedNode->SetDataInteractor(m_ImageNode->GetDataInteractor()); + if (!m_ImageNode.IsExpired()) + selectedNode->SetDataInteractor(m_ImageNode.Lock()->GetDataInteractor()); // m_ImageNode->GetDataInteractor()->SetDataNode(selectedNode); m_ImageNode = selectedNode; } } void QmitkImageCropper::OnSliderValueChanged(int slidervalue) { m_CropOutsideValue = slidervalue; } void QmitkImageCropper::DoMasking() { this->ProcessImage(true); } void QmitkImageCropper::DoCropping() { this->ProcessImage(false); } void QmitkImageCropper::ProcessImage(bool mask) { // cropping only possible if valid bounding shape as well as a valid image are loaded QList nodes = this->GetDataManagerSelection(); auto renderWindowPart = this->GetRenderWindowPart(OPEN); int timeStep = renderWindowPart->GetTimeNavigationController()->GetTime()->GetPos(); if (nodes.empty()) return; mitk::DataNode* node = nodes[0]; if (node == nullptr) { QMessageBox::information(nullptr, "Warning", "Please load and select an image before starting image processing."); return; } if (m_CroppingObject == nullptr) { QMessageBox::information(nullptr, "Warning", "Please load and select a cropping object before starting image processing."); return; } mitk::BaseData* data = node->GetData(); //get data from node if (data != nullptr) { QString imageName; if (mask) { imageName = QString::fromStdString(node->GetName() + "_" + m_CroppingObjectNode->GetName() + "_masked"); } else { imageName = QString::fromStdString(node->GetName() + "_" + m_CroppingObjectNode->GetName() + "_cropped"); } if (m_Controls.checkBoxCropTimeStepOnly->isChecked()) { imageName = imageName + "_T" + QString::number(timeStep); } // image and bounding shape ok, set as input auto croppedImageNode = mitk::DataNode::New(); auto cutter = mitk::BoundingShapeCropper::New(); cutter->SetGeometry(m_CroppingObject); // adjustable in advanced settings cutter->SetUseWholeInputRegion(mask); //either mask (mask=true) or crop (mask=false) cutter->SetOutsideValue(m_CropOutsideValue); cutter->SetUseCropTimeStepOnly(m_Controls.checkBoxCropTimeStepOnly->isChecked()); cutter->SetCurrentTimeStep(timeStep); // TODO: Add support for MultiLayer (right now only Mulitlabel support) mitk::LabelSetImage* labelsetImageInput = dynamic_cast(data); if (labelsetImageInput != nullptr) { cutter->SetInput(labelsetImageInput); // do the actual cutting try { cutter->Update(); } catch (const itk::ExceptionObject& e) { std::string message = std::string("The Cropping filter could not process because of: \n ") + e.GetDescription(); QMessageBox::warning(nullptr, tr("Cropping not possible!"), tr(message.c_str()), QMessageBox::Ok, QMessageBox::NoButton, QMessageBox::NoButton); return; } auto labelSetImage = mitk::LabelSetImage::New(); labelSetImage->InitializeByLabeledImage(cutter->GetOutput()); for (unsigned int i = 0; i < labelsetImageInput->GetNumberOfLayers(); i++) { labelSetImage->AddLabelSetToLayer(i, labelsetImageInput->GetLabelSet(i)); } croppedImageNode->SetData(labelSetImage); croppedImageNode->SetProperty("name", mitk::StringProperty::New(imageName.toStdString())); //add cropping result to the current data storage as child node to the image node if (!m_Controls.checkOverwriteImage->isChecked()) { if (!this->GetDataStorage()->Exists(croppedImageNode)) { - this->GetDataStorage()->Add(croppedImageNode, m_ImageNode); + this->GetDataStorage()->Add(croppedImageNode, m_ImageNode.Lock()); } } else // original image will be overwritten by the result image and the bounding box of the result is adjusted { node->SetData(labelSetImage); node->Modified(); // Adjust coordinate system by doing a reinit on auto tempDataStorage = mitk::DataStorage::SetOfObjects::New(); tempDataStorage->InsertElement(0, node); // initialize the views to the bounding geometry mitk::TimeGeometry::Pointer bounds = this->GetDataStorage()->ComputeBoundingGeometry3D(tempDataStorage); mitk::RenderingManager::GetInstance()->InitializeViews(bounds); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } } else { mitk::Image::Pointer imageInput = dynamic_cast(data); if (imageInput != nullptr) { cutter->SetInput(imageInput); // do the actual cutting try { cutter->Update(); } catch (const itk::ExceptionObject& e) { std::string message = std::string("The Cropping filter could not process because of: \n ") + e.GetDescription(); QMessageBox::warning(nullptr, tr("Cropping not possible!"), tr(message.c_str()), QMessageBox::Ok, QMessageBox::NoButton, QMessageBox::NoButton); return; } //add cropping result to the current data storage as child node to the image node if (!m_Controls.checkOverwriteImage->isChecked()) { croppedImageNode->SetData(cutter->GetOutput()); croppedImageNode->SetProperty("name", mitk::StringProperty::New(imageName.toStdString())); croppedImageNode->SetProperty("color", mitk::ColorProperty::New(1.0, 1.0, 1.0)); croppedImageNode->SetProperty("layer", mitk::IntProperty::New(99)); // arbitrary, copied from segmentation functionality if (!this->GetDataStorage()->Exists(croppedImageNode)) { - this->GetDataStorage()->Add(croppedImageNode, m_ImageNode); + this->GetDataStorage()->Add(croppedImageNode, m_ImageNode.Lock()); } } else // original image will be overwritten by the result image and the bounding box of the result is adjusted { node->SetData(cutter->GetOutput()); node->Modified(); // Adjust coordinate system by doing a reinit on auto tempDataStorage = mitk::DataStorage::SetOfObjects::New(); tempDataStorage->InsertElement(0, node); // initialize the views to the bounding geometry mitk::TimeGeometry::Pointer bounds = this->GetDataStorage()->ComputeBoundingGeometry3D(tempDataStorage); mitk::RenderingManager::GetInstance()->InitializeViews(bounds); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } } } } else { QMessageBox::information(nullptr, "Warning", "Please load and select an image before starting image processing."); } } 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 8dc1efe285..d2cfc1749a 100755 --- a/Plugins/org.mitk.gui.qt.volumevisualization/src/internal/QmitkVolumeVisualizationView.cpp +++ b/Plugins/org.mitk.gui.qt.volumevisualization/src/internal/QmitkVolumeVisualizationView.cpp @@ -1,344 +1,349 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "QmitkVolumeVisualizationView.h" #include #include #include #include #include #include //#include #include #include #include #include #include #include #include "mitkHistogramGenerator.h" #include "QmitkPiecewiseFunctionCanvas.h" #include "QmitkColorTransferFunctionCanvas.h" #include "mitkBaseRenderer.h" #include "mitkVtkVolumeRenderingProperty.h" #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) { } QmitkVolumeVisualizationView::~QmitkVolumeVisualizationView() { } void QmitkVolumeVisualizationView::CreateQtPartControl(QWidget* parent) { if (!m_Controls) { m_Controls = new Ui::QmitkVolumeVisualizationViewControls; m_Controls->setupUi(parent); // Fill the tf presets in the generator widget std::vector names; mitk::TransferFunctionInitializer::GetPresetNames(names); for (std::vector::const_iterator it = names.begin(); it != names.end(); ++it) { m_Controls->m_TransferFunctionGeneratorWidget->AddPreset(QString::fromStdString(*it)); } // see enum in vtkSmartVolumeMapper m_Controls->m_RenderMode->addItem("Default"); m_Controls->m_RenderMode->addItem("RayCast"); m_Controls->m_RenderMode->addItem("GPU"); // see vtkVolumeMapper::BlendModes m_Controls->m_BlendMode->addItem("Comp"); m_Controls->m_BlendMode->addItem("Max"); m_Controls->m_BlendMode->addItem("Min"); m_Controls->m_BlendMode->addItem("Avg"); m_Controls->m_BlendMode->addItem("Add"); connect( m_Controls->m_EnableRenderingCB, SIGNAL( toggled(bool) ),this, SLOT( OnEnableRendering(bool) )); connect(m_Controls->m_RenderMode, SIGNAL(activated(int)), this, SLOT(OnRenderMode(int))); connect(m_Controls->m_BlendMode, SIGNAL(activated(int)), this, SLOT(OnBlendMode(int))); connect( m_Controls->m_TransferFunctionGeneratorWidget, SIGNAL( SignalUpdateCanvas( ) ), m_Controls->m_TransferFunctionWidget, SLOT( OnUpdateCanvas( ) ) ); connect( m_Controls->m_TransferFunctionGeneratorWidget, SIGNAL(SignalTransferFunctionModeChanged(int)), SLOT(OnMitkInternalPreset(int))); m_Controls->m_EnableRenderingCB->setEnabled(false); m_Controls->m_BlendMode->setEnabled(false); m_Controls->m_RenderMode->setEnabled(false); m_Controls->m_TransferFunctionWidget->setEnabled(false); m_Controls->m_TransferFunctionGeneratorWidget->setEnabled(false); m_Controls->m_SelectedImageLabel->hide(); m_Controls->m_ErrorImageLabel->hide(); } } void QmitkVolumeVisualizationView::OnMitkInternalPreset( int mode ) { - if (m_SelectedNode.IsNull()) return; + if (m_SelectedNode.IsExpired()) return; - mitk::DataNode::Pointer node(m_SelectedNode.GetPointer()); + 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->m_TransferFunctionWidget->OnUpdateCanvas(); } } void QmitkVolumeVisualizationView::OnSelectionChanged(berry::IWorkbenchPart::Pointer /*part*/, const QList& nodes) { bool weHadAnImageButItsNotThreeDeeOrFourDee = false; mitk::DataNode::Pointer node; for (mitk::DataNode::Pointer currentNode: nodes) { if( currentNode.IsNotNull() && dynamic_cast(currentNode->GetData()) ) { if( dynamic_cast(currentNode->GetData())->GetDimension()>=3 ) { if (node.IsNull()) { node = currentNode; } } else { weHadAnImageButItsNotThreeDeeOrFourDee = true; } } } if( node.IsNotNull() ) { m_Controls->m_NoSelectedImageLabel->hide(); m_Controls->m_ErrorImageLabel->hide(); m_Controls->m_SelectedImageLabel->show(); std::string infoText; if (node->GetName().empty()) infoText = std::string("Selected Image: [currently selected image has no name]"); else infoText = std::string("Selected Image: ") + node->GetName(); m_Controls->m_SelectedImageLabel->setText( QString( infoText.c_str() ) ); m_SelectedNode = node; } else { if(weHadAnImageButItsNotThreeDeeOrFourDee) { m_Controls->m_NoSelectedImageLabel->hide(); m_Controls->m_ErrorImageLabel->show(); std::string infoText; infoText = std::string("only 3D or 4D images are supported"); m_Controls->m_ErrorImageLabel->setText( QString( infoText.c_str() ) ); } else { m_Controls->m_SelectedImageLabel->hide(); m_Controls->m_ErrorImageLabel->hide(); m_Controls->m_NoSelectedImageLabel->show(); } - m_SelectedNode = 0; + m_SelectedNode = nullptr; } UpdateInterface(); } void QmitkVolumeVisualizationView::UpdateInterface() { - if(m_SelectedNode.IsNull()) + if(m_SelectedNode.IsExpired()) { // turnoff all m_Controls->m_EnableRenderingCB->setChecked(false); m_Controls->m_EnableRenderingCB->setEnabled(false); m_Controls->m_BlendMode->setCurrentIndex(0); m_Controls->m_BlendMode->setEnabled(false); m_Controls->m_RenderMode->setCurrentIndex(0); m_Controls->m_RenderMode->setEnabled(false); m_Controls->m_TransferFunctionWidget->SetDataNode(0); m_Controls->m_TransferFunctionWidget->setEnabled(false); m_Controls->m_TransferFunctionGeneratorWidget->SetDataNode(0); m_Controls->m_TransferFunctionGeneratorWidget->setEnabled(false); return; } bool enabled = false; + auto selectedNode = m_SelectedNode.Lock(); - m_SelectedNode->GetBoolProperty("volumerendering",enabled); + selectedNode->GetBoolProperty("volumerendering",enabled); m_Controls->m_EnableRenderingCB->setEnabled(true); m_Controls->m_EnableRenderingCB->setChecked(enabled); if(!enabled) { // turnoff all except volumerendering checkbox m_Controls->m_BlendMode->setCurrentIndex(0); m_Controls->m_BlendMode->setEnabled(false); m_Controls->m_RenderMode->setCurrentIndex(0); m_Controls->m_RenderMode->setEnabled(false); m_Controls->m_TransferFunctionWidget->SetDataNode(0); m_Controls->m_TransferFunctionWidget->setEnabled(false); m_Controls->m_TransferFunctionGeneratorWidget->SetDataNode(0); m_Controls->m_TransferFunctionGeneratorWidget->setEnabled(false); return; } // otherwise we can activate em all m_Controls->m_BlendMode->setEnabled(true); m_Controls->m_RenderMode->setEnabled(true); // Determine Combo Box mode { bool usegpu=false; bool useray=false; bool usemip=false; - m_SelectedNode->GetBoolProperty("volumerendering.usegpu",usegpu); - m_SelectedNode->GetBoolProperty("volumerendering.useray",useray); - m_SelectedNode->GetBoolProperty("volumerendering.usemip",usemip); + selectedNode->GetBoolProperty("volumerendering.usegpu",usegpu); + selectedNode->GetBoolProperty("volumerendering.useray",useray); + selectedNode->GetBoolProperty("volumerendering.usemip",usemip); int blendMode; - if (m_SelectedNode->GetIntProperty("volumerendering.blendmode", blendMode)) + if (selectedNode->GetIntProperty("volumerendering.blendmode", blendMode)) m_Controls->m_BlendMode->setCurrentIndex(blendMode); if (usemip) m_Controls->m_BlendMode->setCurrentIndex(vtkVolumeMapper::MAXIMUM_INTENSITY_BLEND); int mode = DEFAULT_RENDERMODE; if (useray) mode = RAYCAST_RENDERMODE; else if(usegpu) mode = GPU_RENDERMODE; m_Controls->m_RenderMode->setCurrentIndex(mode); } - m_Controls->m_TransferFunctionWidget->SetDataNode(m_SelectedNode); + m_Controls->m_TransferFunctionWidget->SetDataNode(selectedNode); m_Controls->m_TransferFunctionWidget->setEnabled(true); - m_Controls->m_TransferFunctionGeneratorWidget->SetDataNode(m_SelectedNode); + m_Controls->m_TransferFunctionGeneratorWidget->SetDataNode(selectedNode); m_Controls->m_TransferFunctionGeneratorWidget->setEnabled(true); } void QmitkVolumeVisualizationView::OnEnableRendering(bool state) { - if(m_SelectedNode.IsNull()) + if(m_SelectedNode.IsExpired()) return; - m_SelectedNode->SetProperty("volumerendering",mitk::BoolProperty::New(state)); + m_SelectedNode.Lock()->SetProperty("volumerendering",mitk::BoolProperty::New(state)); UpdateInterface(); RequestRenderWindowUpdate(); } void QmitkVolumeVisualizationView::OnBlendMode(int mode) { - if (m_SelectedNode.IsNull()) + if (m_SelectedNode.IsExpired()) return; + auto selectedNode = m_SelectedNode.Lock(); + bool usemip = false; if (mode == vtkVolumeMapper::MAXIMUM_INTENSITY_BLEND) usemip = true; - m_SelectedNode->SetProperty("volumerendering.usemip", mitk::BoolProperty::New(usemip)); - m_SelectedNode->SetProperty("volumerendering.blendmode", mitk::IntProperty::New(mode)); + selectedNode->SetProperty("volumerendering.usemip", mitk::BoolProperty::New(usemip)); + selectedNode->SetProperty("volumerendering.blendmode", mitk::IntProperty::New(mode)); RequestRenderWindowUpdate(); } void QmitkVolumeVisualizationView::OnRenderMode(int mode) { - if(m_SelectedNode.IsNull()) + if(m_SelectedNode.IsExpired()) return; + + auto selectedNode = m_SelectedNode.Lock(); bool usegpu = false; if (mode == GPU_RENDERMODE) usegpu = true; bool useray = false; if (mode == RAYCAST_RENDERMODE) useray = true; if (mode == DEFAULT_RENDERMODE) { useray = true; usegpu = true; } - m_SelectedNode->SetProperty("volumerendering.usegpu",mitk::BoolProperty::New(usegpu)); - m_SelectedNode->SetProperty("volumerendering.useray",mitk::BoolProperty::New(useray)); + selectedNode->SetProperty("volumerendering.usegpu",mitk::BoolProperty::New(usegpu)); + selectedNode->SetProperty("volumerendering.useray",mitk::BoolProperty::New(useray)); RequestRenderWindowUpdate(); } void QmitkVolumeVisualizationView::SetFocus() { } void QmitkVolumeVisualizationView::NodeRemoved(const mitk::DataNode* node) { if(m_SelectedNode == node) { - m_SelectedNode=0; + m_SelectedNode = nullptr; m_Controls->m_SelectedImageLabel->hide(); m_Controls->m_ErrorImageLabel->hide(); m_Controls->m_NoSelectedImageLabel->show(); UpdateInterface(); } }