diff --git a/Modules/Core/include/mitkCrosshairManager.h b/Modules/Core/include/mitkCrosshairManager.h index 575b9b0ab8..90713fc9fb 100644 --- a/Modules/Core/include/mitkCrosshairManager.h +++ b/Modules/Core/include/mitkCrosshairManager.h @@ -1,105 +1,147 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #ifndef mitkCrosshairManager_h #define mitkCrosshairManager_h #include #include +#include #include -#include -#include - -#include -#include namespace mitk { /** - * \brief The CrosshairManager takes care of the correct settings for the plane geometries - * that form the crosshair. + * \brief The CrosshairManager takes care of the correct settings for the crosshair. * - * The CrosshairManager keeps track of a TimeGeometry, which is used to compute - * the three different plane geometries (axial, coronal, sagittal). - * These three plane geometries can be added to / removed from the data storage - * using this CrosshairManager. Adding / removing them to / from the data storage + * The CrosshairManager keeps track of a crosshair data node, + * which is used to compute the lines of a crosshair. + * These crosshair data node can be added to / removed from the data storage + * using this CrosshairManager. Adding / removing the node to / from the data storage * will change the rendering behavior for the crosshair - only data nodes * inside the data storage will be rendered. - * - * */ class MITKCORE_EXPORT CrosshairManager : public itk::Object { public: mitkClassMacroItkParent(CrosshairManager, itk::Object); - mitkNewMacro2Param(Self, DataStorage*, BaseRenderer*); - - itkSetObjectMacro(DataStorage, DataStorage); - itkSetObjectMacro(BaseRenderer, BaseRenderer); - - /** - * \brief Set the input time geometry out of which the oriented geometries will be created. - */ - void ComputeOrientedTimeGeometries(const TimeGeometry* geometry); + mitkNewMacro1Param(Self, BaseRenderer*); + /** + * \brief Set the position of the managed crosshair to the given 3D position. + * + * \pre The crosshair data of this manager needs to be not null in order to + * set a new position. + * \throw mitk::Exception, if the crosshair data of this manager is null. + * + * \param selectedPoint The 3D point that will be newly set for the crosshair data. + */ void SetCrosshairPosition(const Point3D& selectedPoint); + /** + * \brief Update the position of the managed crosshair to the current slice + * position of the given slice navigation controller, depending on the view + * direction of the slice navigation controller. + * + * \pre The crosshair data of this manager needs to be not null in order to + * update the crosshair position. + * \throw mitk::Exception, if the crosshair data of this manager is null. + * + * \param sliceNavigationController The slice navigation controller whose current position + * will be used for updating the corresponding 3D coordinate + * of the crosshair data's position. + */ + void UpdateCrosshairPosition(const SliceNavigationController* sliceNavigationController); + /** + * \brief Get the position of the managed crosshair. + * + * \pre The crosshair data of this manager needs to be not null in order to + * get the current crosshair position. + * \throw mitk::Exception, if the crosshair data of this manager is null. + * + * \return The 3D point that represents the position of the crosshair data. + */ Point3D GetCrosshairPosition() const; - - void UpdateSlice(const SliceNavigationController* sliceNavigationController); - - void SetCrosshairVisibility(bool visible); - bool GetCrosshairVisibility() const; + /** + * \brief Set the visibility of the managed crosshair to the given value. + * + * \pre The crosshair data of this manager needs to be not null in order to + * set the visibility. + * \throw mitk::Exception, if the crosshair data of this manager is null. + * + * \param visible A boolean value that will define the visibility of the crosshair. + * \param baseRenderer A base renderer for which the renderer-specific visibility property + * should be set. + */ + void SetCrosshairVisibility(bool visible, const BaseRenderer* baseRenderer); + /** + * \brief Get the visibility of the managed crosshair. + * + * \pre The crosshair data of this manager needs to be not null in order to + * get the visibility. + * \throw mitk::Exception, if the crosshair data of this manager is null. + * + * \param baseRenderer A base renderer for which the renderer-specific visibility property + * should be get. + * \return The boolean value that represents the visibility of the crosshair. + */ + bool GetCrosshairVisibility(const BaseRenderer* baseRenderer) const; + /** + * \brief Set the gap size of the managed crosshair to the given value. + * + * \pre The crosshair data of this manager needs to be not null in order to + * set the gap size. + * \throw mitk::Exception, if the crosshair data of this manager is null. + * + * \param gapSize The size of the gap in the center of the crosshair. + */ void SetCrosshairGap(unsigned int gapSize); - - void AddPlanesToDataStorage(); - void RemovePlanesFromDataStorage(); + /** + * \brief Add the managed crosshair data node to the given data storage. + * + * \pre The crosshair data node of this manager needs to be not null in order to + * add the node to the data storage. + * \throw mitk::Exception, if the crosshair data node of this manager is null. + * \pre The given data storage needs to be not null in order to add the node to + * the data storage. + * \throw mitk::Exception, if the given data storage is null. + * + * \param dataStorage The data storage to which the crosshair data node should be added. + */ + void AddCrosshairNodeToDataStorage(DataStorage* dataStorage); + /** + * \brief Remove the managed crosshair data node from the given data storage. + * + * \pre The crosshair data node of this manager needs to be not null in order to + * remove the node from the data storage. + * \throw mitk::Exception, if the crosshair data node of this manager is null. + * \pre The given data storage needs to be not null in order to remove the node + * from the data storage. + * \throw mitk::Exception, if the given data storage is null. + * + * \param dataStorage The data storage from which the crosshair data node should be removed. + */ + void RemoveCrosshairNodeFromDataStorage(DataStorage* dataStorage); protected: - CrosshairManager(DataStorage* dataStorage, BaseRenderer* baseRenderer); + CrosshairManager(BaseRenderer* baseRenderer); ~CrosshairManager(); - void InitializePlaneProperties(DataNode::Pointer planeNode, const std::string& planeName); - void InitializePlaneData(DataNode::Pointer planeNode, const TimeGeometry* timeGeometry, unsigned int& slice); - void UpdatePlaneSlice(DataNode::Pointer planeNode, const TimeGeometry* timeGeometry, unsigned int slice); - void SetCrosshairPosition(const Point3D& selectedPoint, - DataNode::Pointer planeNode, - const TimeGeometry* timeGeometry, - unsigned int& slice); - void AddPlaneToDataStorage(DataNode::Pointer planeNode, DataNode::Pointer parent); - - DataStorage* m_DataStorage; - BaseRenderer* m_BaseRenderer; - - TimeGeometry::ConstPointer m_InputTimeGeometry; - TimeGeometry::Pointer m_AxialTimeGeometry; - TimeGeometry::Pointer m_CoronalTimeGeometry; - TimeGeometry::Pointer m_SagittalTimeGeometry; - - unsigned int m_AxialSlice; - unsigned int m_CoronalSlice; - unsigned int m_SagittalSlice; - - /** - * @brief The 3 helper objects which contain the plane geometry. - */ - DataNode::Pointer m_AxialPlaneNode; - DataNode::Pointer m_CoronalPlaneNode; - DataNode::Pointer m_SagittalPlaneNode; - DataNode::Pointer m_ParentNodeForGeometryPlanes; + DataNode::Pointer m_CrosshairDataNode; + CrosshairData::Pointer m_CrosshairData; }; -} // namespace mitk +} #endif diff --git a/Modules/Core/src/Controllers/mitkCrosshairManager.cpp b/Modules/Core/src/Controllers/mitkCrosshairManager.cpp index 8bd39e7449..2a5d497f4c 100644 --- a/Modules/Core/src/Controllers/mitkCrosshairManager.cpp +++ b/Modules/Core/src/Controllers/mitkCrosshairManager.cpp @@ -1,414 +1,167 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #include "mitkCrosshairManager.h" -#include -#include -#include +#include #include -#include -#include - -mitk::CrosshairManager::CrosshairManager(DataStorage* dataStorage, BaseRenderer* baseRenderer) - : m_DataStorage(dataStorage) - , m_BaseRenderer(baseRenderer) - , m_InputTimeGeometry() - , m_AxialTimeGeometry() - , m_CoronalTimeGeometry() - , m_SagittalTimeGeometry() -{ - m_AxialPlaneNode = mitk::DataNode::New(); - m_CoronalPlaneNode = mitk::DataNode::New(); - m_SagittalPlaneNode = mitk::DataNode::New(); - - std::string rendererName = std::string(m_BaseRenderer->GetName()); - this->InitializePlaneProperties(m_AxialPlaneNode, std::string(rendererName + "axial.plane")); - this->InitializePlaneProperties(m_CoronalPlaneNode, std::string(rendererName + "coronal.plane")); - this->InitializePlaneProperties(m_SagittalPlaneNode, std::string(rendererName + "sagittal.plane")); - - m_ParentNodeForGeometryPlanes = mitk::DataNode::New(); - m_ParentNodeForGeometryPlanes->SetProperty("name", mitk::StringProperty::New(rendererName)); - m_ParentNodeForGeometryPlanes->SetProperty("helper object", mitk::BoolProperty::New(true)); -} -mitk::CrosshairManager::~CrosshairManager() -{ - this->RemovePlanesFromDataStorage(); -} +#include -void mitk::CrosshairManager::ComputeOrientedTimeGeometries(const TimeGeometry* geometry) +mitk::CrosshairManager::CrosshairManager(BaseRenderer* baseRenderer) { - if (nullptr != geometry) - { - if (geometry->GetBoundingBoxInWorld()->GetDiagonalLength2() < eps) - { - itkWarningMacro("setting an empty bounding-box"); - geometry = nullptr; - } - } - - if (m_InputTimeGeometry == geometry) - { - return; - } - - m_InputTimeGeometry = geometry; + m_CrosshairDataNode = mitk::DataNode::New(); - if (m_InputTimeGeometry.IsNull()) - { - return; - } + std::string rendererName = std::string(baseRenderer->GetName()); - if (0 == m_InputTimeGeometry->CountTimeSteps()) - { - return; - } + m_CrosshairDataNode->SetProperty("reslice.thickslices", mitk::ResliceMethodProperty::New()); + m_CrosshairDataNode->SetProperty("reslice.thickslices.num", mitk::IntProperty::New(5)); + m_CrosshairDataNode->SetProperty("Crosshair.Gap Size", mitk::IntProperty::New(32)); - try - { - m_AxialTimeGeometry = SliceNavigationHelper::CreateOrientedTimeGeometry( - m_InputTimeGeometry, AnatomicalPlane::Axial, false, false, true); - m_CoronalTimeGeometry = SliceNavigationHelper::CreateOrientedTimeGeometry( - m_InputTimeGeometry, AnatomicalPlane::Coronal, false, true, false); - m_SagittalTimeGeometry = SliceNavigationHelper::CreateOrientedTimeGeometry( - m_InputTimeGeometry, AnatomicalPlane::Sagittal, true, true, false); - } - catch (const mitk::Exception& e) - { - MITK_ERROR << "Unable to create oriented time geometries\n" - << "Reason: " << e.GetDescription(); - } + // set the crosshair only visible for this specific renderer + m_CrosshairDataNode->SetVisibility(false); + m_CrosshairDataNode->SetVisibility(true, baseRenderer); + m_CrosshairDataNode->SetProperty("name", mitk::StringProperty::New(std::string(rendererName + "crosshairData"))); + m_CrosshairDataNode->SetProperty("helper object", mitk::BoolProperty::New(true)); - this->InitializePlaneData(m_AxialPlaneNode, m_AxialTimeGeometry, m_AxialSlice); - this->InitializePlaneData(m_CoronalPlaneNode, m_CoronalTimeGeometry, m_CoronalSlice); - this->InitializePlaneData(m_SagittalPlaneNode, m_SagittalTimeGeometry, m_SagittalSlice); + m_CrosshairData = CrosshairData::New(); + m_CrosshairDataNode->SetData(m_CrosshairData); + m_CrosshairDataNode->SetMapper(mitk::BaseRenderer::Standard2D, mitk::CrosshairVtkMapper2D::New()); +} - RenderingManager::GetInstance()->RequestUpdate(m_BaseRenderer->GetRenderWindow()); +mitk::CrosshairManager::~CrosshairManager() +{ } void mitk::CrosshairManager::SetCrosshairPosition(const Point3D& selectedPoint) { - if (m_AxialPlaneNode.IsNull() || m_CoronalPlaneNode.IsNull() || m_SagittalPlaneNode.IsNull()) + if (m_CrosshairData.IsNull()) { - return; + mitkThrow() << "No crosshair data available. Crosshair is in an invalid state."; } - if (m_AxialTimeGeometry.IsNull() || m_CoronalTimeGeometry.IsNull() || m_SagittalTimeGeometry.IsNull()) - { - return; - } - - this->SetCrosshairPosition(selectedPoint, m_AxialPlaneNode, m_AxialTimeGeometry, m_AxialSlice); - this->SetCrosshairPosition(selectedPoint, m_CoronalPlaneNode, m_CoronalTimeGeometry, m_CoronalSlice); - this->SetCrosshairPosition(selectedPoint, m_SagittalPlaneNode, m_SagittalTimeGeometry, m_SagittalSlice); + m_CrosshairData->SetPosition(selectedPoint); } -mitk::Point3D mitk::CrosshairManager::GetCrosshairPosition() const +void mitk::CrosshairManager::UpdateCrosshairPosition(const SliceNavigationController* sliceNavigationController) { - if (m_InputTimeGeometry.IsNull()) + if (m_CrosshairData.IsNull()) { - // return null-point since we don't show the crosshair anyway - return Point3D(0.0); + mitkThrow() << "No crosshair data available. Crosshair is in an invalid state."; } - Point3D point = m_InputTimeGeometry->GetCenterInWorld(); - - PlaneGeometry* axialPlaneGeometry = nullptr; - PlaneGeometry* coronalPlaneGeometry = nullptr; - PlaneGeometry* sagittalPlaneGeometry = nullptr; + Point3D crosshairPosition = m_CrosshairData->GetPosition(); - // get the currently selected time point - auto selectedTimePoint = RenderingManager::GetInstance()->GetTimeNavigationController()->GetSelectedTimePoint(); - try - { - axialPlaneGeometry = - SliceNavigationHelper::GetCurrentPlaneGeometry(m_AxialTimeGeometry, selectedTimePoint, m_AxialSlice); - coronalPlaneGeometry = - SliceNavigationHelper::GetCurrentPlaneGeometry(m_CoronalTimeGeometry, selectedTimePoint, m_CoronalSlice); - sagittalPlaneGeometry = - SliceNavigationHelper::GetCurrentPlaneGeometry(m_SagittalTimeGeometry, selectedTimePoint, m_SagittalSlice); - } - catch (const mitk::Exception& e) - { - MITK_ERROR << "Unable to get plane geometries. Using default center point.\n" - << "Reason: " << e.GetDescription(); - } - - Line3D line; - if (nullptr != axialPlaneGeometry && nullptr != coronalPlaneGeometry && - axialPlaneGeometry->IntersectionLine(coronalPlaneGeometry, line)) - { - if (nullptr != sagittalPlaneGeometry && sagittalPlaneGeometry->IntersectionPoint(line, point)) - { - return point; - } - } - - // return input geometry center point if no intersection point was found - return point; -} - -void mitk::CrosshairManager::UpdateSlice(const SliceNavigationController* sliceNavigationController) -{ auto viewDirection = sliceNavigationController->GetViewDirection(); unsigned int slicePosition = sliceNavigationController->GetSlice()->GetPos(); switch (viewDirection) { case AnatomicalPlane::Original: return; case AnatomicalPlane::Axial: { - m_AxialSlice = slicePosition; - this->UpdatePlaneSlice(m_AxialPlaneNode, m_AxialTimeGeometry, m_AxialSlice); + crosshairPosition[2] = slicePosition; break; } case AnatomicalPlane::Coronal: { - m_CoronalSlice = slicePosition; - this->UpdatePlaneSlice(m_CoronalPlaneNode, m_CoronalTimeGeometry, m_CoronalSlice); + crosshairPosition[1] = slicePosition; break; } case AnatomicalPlane::Sagittal: { - m_SagittalSlice = slicePosition; - this->UpdatePlaneSlice(m_SagittalPlaneNode, m_SagittalTimeGeometry, m_SagittalSlice); + crosshairPosition[0] = slicePosition; break; } } -} -void mitk::CrosshairManager::SetCrosshairVisibility(bool visible) -{ - if (m_AxialPlaneNode.IsNull() || m_CoronalPlaneNode.IsNull() || m_SagittalPlaneNode.IsNull()) - { - return; - } - - m_AxialPlaneNode->SetVisibility(visible, m_BaseRenderer); - m_CoronalPlaneNode->SetVisibility(visible, m_BaseRenderer); - m_SagittalPlaneNode->SetVisibility(visible, m_BaseRenderer); + m_CrosshairData->SetPosition(crosshairPosition); } -bool mitk::CrosshairManager::GetCrosshairVisibility() const +mitk::Point3D mitk::CrosshairManager::GetCrosshairPosition() const { - if (m_AxialPlaneNode.IsNull() || m_CoronalPlaneNode.IsNull() || m_SagittalPlaneNode.IsNull()) - { - return false; - } - - bool axialVisibility = false; - if (m_AxialPlaneNode->GetVisibility(axialVisibility, m_BaseRenderer)) + if (m_CrosshairData.IsNull()) { - bool coronalVisibility = false; - if (m_CoronalPlaneNode->GetVisibility(coronalVisibility, m_BaseRenderer)) - { - bool sagittalVisibility = false; - if (m_SagittalPlaneNode->GetVisibility(sagittalVisibility, m_BaseRenderer)) - { - if (axialVisibility == coronalVisibility && coronalVisibility == sagittalVisibility) - { - return axialVisibility; - } - } - } + mitkThrow() << "No crosshair data available. Crosshair is in an invalid state."; } - mitkThrow() << "Invalid state of plane visibility."; -} - -void mitk::CrosshairManager::SetCrosshairGap(unsigned int gapSize) -{ - m_AxialPlaneNode->SetIntProperty("Crosshair.Gap Size", gapSize); - m_CoronalPlaneNode->SetIntProperty("Crosshair.Gap Size", gapSize); - m_SagittalPlaneNode->SetIntProperty("Crosshair.Gap Size", gapSize); + return m_CrosshairData->GetPosition(); } -void mitk::CrosshairManager::AddPlanesToDataStorage() +void mitk::CrosshairManager::SetCrosshairVisibility(bool visible, const BaseRenderer* baseRenderer) { - if (nullptr == m_DataStorage) + if (m_CrosshairDataNode.IsNull()) { - return; + mitkThrow() << "No crosshair data node available. Crosshair is in an invalid state."; } - if (m_AxialPlaneNode.IsNull() || m_CoronalPlaneNode.IsNull() - || m_SagittalPlaneNode.IsNull() || m_ParentNodeForGeometryPlanes.IsNull()) - { - return; - } - - this->AddPlaneToDataStorage(m_ParentNodeForGeometryPlanes, nullptr); - this->AddPlaneToDataStorage(m_AxialPlaneNode, m_ParentNodeForGeometryPlanes); - this->AddPlaneToDataStorage(m_CoronalPlaneNode, m_ParentNodeForGeometryPlanes); - this->AddPlaneToDataStorage(m_SagittalPlaneNode, m_ParentNodeForGeometryPlanes); + m_CrosshairDataNode->SetVisibility(visible, baseRenderer); } -void mitk::CrosshairManager::RemovePlanesFromDataStorage() +bool mitk::CrosshairManager::GetCrosshairVisibility(const BaseRenderer* baseRenderer) const { - if (nullptr == m_DataStorage) + if (m_CrosshairDataNode.IsNull()) { - return; + mitkThrow() << "No crosshair data node available. Crosshair is in an invalid state."; } - if (m_AxialPlaneNode.IsNotNull() && m_CoronalPlaneNode.IsNotNull() - && m_SagittalPlaneNode.IsNotNull() && m_ParentNodeForGeometryPlanes.IsNotNull()) + bool visibility = false; + if (m_CrosshairDataNode->GetVisibility(visibility, baseRenderer)) { - m_DataStorage->Remove(m_AxialPlaneNode); - m_DataStorage->Remove(m_CoronalPlaneNode); - m_DataStorage->Remove(m_SagittalPlaneNode); - m_DataStorage->Remove(m_ParentNodeForGeometryPlanes); + return false; } -} - -void mitk::CrosshairManager::InitializePlaneProperties(DataNode::Pointer planeNode, const std::string& planeName) -{ - planeNode->SetProperty("reslice.thickslices", mitk::ResliceMethodProperty::New()); - planeNode->SetProperty("reslice.thickslices.num", mitk::IntProperty::New(5)); - planeNode->SetProperty("Crosshair.Gap Size", mitk::IntProperty::New(32)); - // set the crosshair only visible for this specific renderer - planeNode->SetVisibility(false); - planeNode->SetVisibility(true, m_BaseRenderer); - planeNode->SetProperty("name", mitk::StringProperty::New(planeName)); - planeNode->SetProperty("helper object", mitk::BoolProperty::New(true)); + return visibility; } -void mitk::CrosshairManager::InitializePlaneData(DataNode::Pointer planeNode, const TimeGeometry* timeGeometry, unsigned int& slice) +void mitk::CrosshairManager::SetCrosshairGap(unsigned int gapSize) { - if (planeNode.IsNull()) + if (m_CrosshairDataNode.IsNull()) { - return; + mitkThrow() << "No crosshair data node available. Crosshair is in an invalid state."; } - if (nullptr == timeGeometry) - { - return; - } - - // get the BaseGeometry of the selected time point - auto selectedTimePoint = RenderingManager::GetInstance()->GetTimeNavigationController()->GetSelectedTimePoint(); - auto currentGeometry = timeGeometry->GetGeometryForTimePoint(selectedTimePoint); - if (nullptr == currentGeometry) - { - // time point not valid for the time geometry - mitkThrow() << "Cannot extract a base geometry. A time point is selected that is not covered by" - << "the given time geometry. Selected time point: " << selectedTimePoint; - } - - const auto* slicedGeometry = dynamic_cast(currentGeometry.GetPointer()); - if (nullptr == slicedGeometry) - { - return; - } - - slice = slicedGeometry->GetSlices() / 2; // center slice - PlaneGeometryData::Pointer planeData = PlaneGeometryData::New(); - planeData->SetPlaneGeometry(slicedGeometry->GetPlaneGeometry(slice)); - planeNode->SetData(planeData); - planeNode->SetMapper(mitk::BaseRenderer::Standard2D, mitk::PlaneGeometryDataMapper2D::New()); + m_CrosshairDataNode->SetIntProperty("Crosshair.Gap Size", gapSize); } -void mitk::CrosshairManager::UpdatePlaneSlice(DataNode::Pointer planeNode, const TimeGeometry* timeGeometry, unsigned int slice) +void mitk::CrosshairManager::AddCrosshairNodeToDataStorage(DataStorage* dataStorage) { - mitk::PlaneGeometry* planeGeometry = nullptr; - // get the currently selected time point - auto selectedTimePoint = RenderingManager::GetInstance()->GetTimeNavigationController()->GetSelectedTimePoint(); - try + if (m_CrosshairDataNode.IsNull()) { - planeGeometry = SliceNavigationHelper::GetCurrentPlaneGeometry(timeGeometry, selectedTimePoint, slice); - } - catch (const mitk::Exception& e) - { - MITK_ERROR << "Unable to get plane geometries\n" - << "Reason: " << e.GetDescription(); + mitkThrow() << "No crosshair data node available. Crosshair is in an invalid state."; } - if (nullptr == planeGeometry) + if (nullptr == dataStorage) { - return; + mitkThrow() << "Datastorage is invalid. Cannot add crosshair node."; } - PlaneGeometryData::Pointer planeData = dynamic_cast(planeNode->GetData()); - if (nullptr == planeData) + if (!dataStorage->Exists(m_CrosshairDataNode)) { - return; + dataStorage->Add(m_CrosshairDataNode); } - - planeData->SetPlaneGeometry(planeGeometry); - planeNode->SetData(planeData); } -void mitk::CrosshairManager::SetCrosshairPosition(const Point3D& selectedPoint, - DataNode::Pointer planeNode, - const TimeGeometry* timeGeometry, - unsigned int& slice) +void mitk::CrosshairManager::RemoveCrosshairNodeFromDataStorage(DataStorage* dataStorage) { - int selectedSlice = -1; - try - { - selectedSlice = SliceNavigationHelper::SelectSliceByPoint(timeGeometry, selectedPoint); - } - catch (const mitk::Exception& e) + if (m_CrosshairDataNode.IsNull()) { - MITK_ERROR << "Unable to select a slice by the given point " << selectedPoint << "\n" - << "Reason: " << e.GetDescription(); + mitkThrow() << "No crosshair data node available. Crosshair is in an invalid state."; } - if (-1 == selectedSlice) + if (nullptr == dataStorage) { - return; + mitkThrow() << "Datastorage is invalid. Cannot remove crosshair node."; } - slice = selectedSlice; - mitk::PlaneGeometry* planeGeometry = nullptr; - // get the currently selected time point - auto selectedTimePoint = RenderingManager::GetInstance()->GetTimeNavigationController()->GetSelectedTimePoint(); - try - { - planeGeometry = SliceNavigationHelper::GetCurrentPlaneGeometry(timeGeometry, selectedTimePoint, slice); - } - catch (const mitk::Exception& e) - { - MITK_ERROR << "Unable to get plane geometries\n" - << "Reason: " << e.GetDescription(); - } - - if (nullptr == planeGeometry) - { - return; - } - - PlaneGeometryData::Pointer planeData = dynamic_cast(planeNode->GetData()); - if (nullptr == planeData) - { - return; - } - - planeData->SetPlaneGeometry(planeGeometry); - planeNode->SetData(planeData); -} - -void mitk::CrosshairManager::AddPlaneToDataStorage(DataNode::Pointer planeNode, DataNode::Pointer parent) -{ - if (!m_DataStorage->Exists(planeNode) - && (nullptr == parent || m_DataStorage->Exists(parent))) - { - try - { - m_DataStorage->Add(planeNode, parent); - } - catch (std::invalid_argument& /*e*/) - { - return; - } - } + dataStorage->Remove(m_CrosshairDataNode); } diff --git a/Modules/QtWidgets/include/QmitkMxNMultiWidget.h b/Modules/QtWidgets/include/QmitkMxNMultiWidget.h index 8cd87a16d5..702191a81e 100644 --- a/Modules/QtWidgets/include/QmitkMxNMultiWidget.h +++ b/Modules/QtWidgets/include/QmitkMxNMultiWidget.h @@ -1,135 +1,135 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #ifndef QmitkMxNMultiWidget_h #define QmitkMxNMultiWidget_h #include "MitkQtWidgetsExports.h" // qt widgets module #include "QmitkAbstractMultiWidget.h" #include #include #include class QSplitter; /** * @brief The 'QmitkMxNMultiWidget' is a 'QmitkAbstractMultiWidget' that is used to display multiple render windows at once. * Render windows can dynamically be added and removed to change the layout of the multi widget. This * is done by using the 'SetLayout'-function to define a layout. This will automatically add or remove * the appropriate number of render window widgets. */ class MITKQTWIDGETS_EXPORT QmitkMxNMultiWidget : public QmitkAbstractMultiWidget { Q_OBJECT public: QmitkMxNMultiWidget(QWidget* parent = nullptr, Qt::WindowFlags f = 0, const QString& multiWidgetName = "mxnmulti"); ~QmitkMxNMultiWidget(); void InitializeMultiWidget() override; void Synchronize(bool synchronized) override; QmitkRenderWindow* GetRenderWindow(const QString& widgetName) const override; QmitkRenderWindow* GetRenderWindow(const mitk::AnatomicalPlane& orientation) const override; void SetActiveRenderWindowWidget(RenderWindowWidgetPointer activeRenderWindowWidget) override; /** * @brief Initialize the active render windows of the MxNMultiWidget to the given geometry. * * @param geometry The geometry to be used to initialize / update the * active render window's time and slice navigation controller. * @param resetCamera If true, the camera and crosshair will be reset to the default view (centered, no zoom). * If false, the current crosshair position and the camera zoom will be stored and reset * after the reference geometry has been updated. */ void InitializeViews(const mitk::TimeGeometry* geometry, bool resetCamera) override; /** * @brief Forward the given time geometry to all base renderers, so that they can store it as their * interaction reference geometry. * This will update the alignment status of the reference geometry for each base renderer. * For more details, see 'BaseRenderer::SetInteractionReferenceGeometry'. * Overridem from 'QmitkAbstractMultiWidget'. */ void SetInteractionReferenceGeometry(const mitk::TimeGeometry* referenceGeometry) override; /** * @brief Returns true if the render windows are coupled; false if not. * * For the MxNMultiWidget the render windows are typically decoupled. */ bool HasCoupledRenderWindows() const override; void SetSelectedPosition(const mitk::Point3D& newPosition, const QString& widgetName) override; const mitk::Point3D GetSelectedPosition(const QString& widgetName) const override; void SetCrosshairVisibility(bool visible) override; bool GetCrosshairVisibility() const override; void SetCrosshairGap(unsigned int gapSize) override; void ResetCrosshair() override; void SetWidgetPlaneMode(int userMode) override; mitk::SliceNavigationController* GetTimeNavigationController(); - void AddPlanesToDataStorage(); - void RemovePlanesFromDataStorage(); + void EnableCrosshair(); + void DisableCrosshair(); public Q_SLOTS: // mouse events void wheelEvent(QWheelEvent* e) override; void mousePressEvent(QMouseEvent* e) override; void moveEvent(QMoveEvent* e) override; void LoadLayout(const nlohmann::json* jsonData); void SaveLayout(std::ostream* outStream); Q_SIGNALS: void WheelMoved(QWheelEvent *); void Moved(); void UpdateUtilityWidgetViewPlanes(); protected: void RemoveRenderWindowWidget() override; private: void SetLayoutImpl() override; void SetInteractionSchemeImpl() override { } QmitkAbstractMultiWidget::RenderWindowWidgetPointer CreateRenderWindowWidget(); void SetInitialSelection(); void ToggleSynchronization(QmitkSynchronizedNodeSelectionWidget* synchronizedWidget); static nlohmann::json BuildJSONFromLayout(const QSplitter* splitter); QSplitter* BuildLayoutFromJSON(const nlohmann::json* jsonData, unsigned int* windowCounter, QSplitter* parentSplitter = nullptr); mitk::SliceNavigationController* m_TimeNavigationController; std::unique_ptr m_SynchronizedWidgetConnector; bool m_CrosshairVisibility; }; #endif diff --git a/Modules/QtWidgets/include/QmitkRenderWindowWidget.h b/Modules/QtWidgets/include/QmitkRenderWindowWidget.h index 6716bdf177..98f734fec0 100644 --- a/Modules/QtWidgets/include/QmitkRenderWindowWidget.h +++ b/Modules/QtWidgets/include/QmitkRenderWindowWidget.h @@ -1,123 +1,123 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #ifndef QmitkRenderWindowWidget_h #define QmitkRenderWindowWidget_h #include "MitkQtWidgetsExports.h" // qt widgets module #include // mitk core #include #include #include // qt #include #include #include class vtkCornerAnnotation; /** * @brief The 'QmitkRenderWindowWidget' is a QFrame that holds a render window * and some associates properties, e.g. decorations. * Decorations are corner annotation (text and color), frame color or background color * and can be set using this class. * The 'QmitkRenderWindowWidget' is used inside a 'QmitkAbstractMultiWidget', where a map contains * several render window widgets to create the multi widget display. * This class uses a CrosshairManager, which allows to use plane geometries as crosshair. */ class MITKQTWIDGETS_EXPORT QmitkRenderWindowWidget : public QFrame { Q_OBJECT public: QmitkRenderWindowWidget( QWidget* parent = nullptr, const QString& widgetName = "", mitk::DataStorage* dataStorage = nullptr); ~QmitkRenderWindowWidget() override; void SetDataStorage(mitk::DataStorage* dataStorage); const QString& GetWidgetName() const { return m_WidgetName; }; QmitkRenderWindow* GetRenderWindow() const { return m_RenderWindow; }; mitk::SliceNavigationController* GetSliceNavigationController() const; void RequestUpdate(); void ForceImmediateUpdate(); void AddUtilityWidget(QWidget* utilityWidget); void SetGradientBackgroundColors(const mitk::Color& upper, const mitk::Color& lower); void ShowGradientBackground(bool enable); std::pair GetGradientBackgroundColors() const { return m_GradientBackgroundColors; }; bool IsGradientBackgroundOn() const; void SetDecorationColor(const mitk::Color& color); mitk::Color GetDecorationColor() const { return m_DecorationColor; }; void ShowColoredRectangle(bool show); bool IsColoredRectangleVisible() const; void ShowCornerAnnotation(bool show); bool IsCornerAnnotationVisible() const; void SetCornerAnnotationText(const std::string& cornerAnnotation); std::string GetCornerAnnotationText() const; bool IsRenderWindowMenuActivated() const; void SetCrosshairVisibility(bool visible); bool GetCrosshairVisibility(); void SetCrosshairGap(unsigned int gapSize); - void AddPlanesToDataStorage(); - void RemovePlanesFromDataStorage(); + void EnableCrosshair(); + void DisableCrosshair(); void SetCrosshairPosition(const mitk::Point3D& newPosition); mitk::Point3D GetCrosshairPosition() const; void SetGeometry(const itk::EventObject& event); void SetGeometrySlice(const itk::EventObject& event); public Q_SLOTS: void OnResetGeometry(); private: void InitializeGUI(); void InitializeDecorations(); void ResetGeometry(const mitk::TimeGeometry* referenceGeometry); QString m_WidgetName; QVBoxLayout* m_Layout; mitk::DataStorage* m_DataStorage; QmitkRenderWindow* m_RenderWindow; mitk::CrosshairManager::Pointer m_CrosshairManager; std::pair m_GradientBackgroundColors; mitk::Color m_DecorationColor; vtkSmartPointer m_CornerAnnotation; }; #endif diff --git a/Modules/QtWidgets/src/QmitkMxNMultiWidget.cpp b/Modules/QtWidgets/src/QmitkMxNMultiWidget.cpp index 095cd35522..a7d674b631 100644 --- a/Modules/QtWidgets/src/QmitkMxNMultiWidget.cpp +++ b/Modules/QtWidgets/src/QmitkMxNMultiWidget.cpp @@ -1,630 +1,630 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #include "QmitkMxNMultiWidget.h" // mitk core #include #include #include #include #include #include // mitk qt widget #include #include // qt #include #include #include #include QmitkMxNMultiWidget::QmitkMxNMultiWidget(QWidget* parent, Qt::WindowFlags f/* = 0*/, const QString& multiWidgetName/* = "mxnmulti"*/) : QmitkAbstractMultiWidget(parent, f, multiWidgetName) , m_TimeNavigationController(nullptr) , m_SynchronizedWidgetConnector(std::make_unique()) , m_CrosshairVisibility(false) { m_TimeNavigationController = mitk::RenderingManager::GetInstance()->GetTimeNavigationController(); } QmitkMxNMultiWidget::~QmitkMxNMultiWidget() { auto allRenderWindows = this->GetRenderWindows(); for (auto& renderWindow : allRenderWindows) { m_TimeNavigationController->Disconnect(renderWindow->GetSliceNavigationController()); } } void QmitkMxNMultiWidget::InitializeMultiWidget() { SetLayout(1, 1); SetDisplayActionEventHandler(std::make_unique()); auto displayActionEventHandler = GetDisplayActionEventHandler(); if (nullptr != displayActionEventHandler) { displayActionEventHandler->InitActions(); } this->SetInitialSelection(); } void QmitkMxNMultiWidget::Synchronize(bool synchronized) { if (synchronized) { SetDisplayActionEventHandler(std::make_unique()); } else { SetDisplayActionEventHandler(std::make_unique()); } auto displayActionEventHandler = GetDisplayActionEventHandler(); if (nullptr != displayActionEventHandler) { displayActionEventHandler->InitActions(); } } QmitkRenderWindow* QmitkMxNMultiWidget::GetRenderWindow(const QString& widgetName) const { if ("axial" == widgetName || "sagittal" == widgetName || "coronal" == widgetName || "3d" == widgetName) { return GetActiveRenderWindowWidget()->GetRenderWindow(); } return QmitkAbstractMultiWidget::GetRenderWindow(widgetName); } QmitkRenderWindow* QmitkMxNMultiWidget::GetRenderWindow(const mitk::AnatomicalPlane& /*orientation*/) const { // currently no mapping between plane orientation and render windows // simply return the currently active render window return GetActiveRenderWindowWidget()->GetRenderWindow(); } void QmitkMxNMultiWidget::SetActiveRenderWindowWidget(RenderWindowWidgetPointer activeRenderWindowWidget) { auto currentActiveRenderWindowWidget = GetActiveRenderWindowWidget(); if (currentActiveRenderWindowWidget == activeRenderWindowWidget) { return; } // reset the decoration color of the previously active render window widget if (nullptr != currentActiveRenderWindowWidget) { auto decorationColor = currentActiveRenderWindowWidget->GetDecorationColor(); QColor hexColor(decorationColor[0] * 255, decorationColor[1] * 255, decorationColor[2] * 255); currentActiveRenderWindowWidget->setStyleSheet("QmitkRenderWindowWidget { border: 2px solid " + hexColor.name(QColor::HexRgb) + "; }"); } // set the new decoration color of the currently active render window widget if (nullptr != activeRenderWindowWidget) { activeRenderWindowWidget->setStyleSheet("QmitkRenderWindowWidget { border: 2px solid #FF6464; }"); } QmitkAbstractMultiWidget::SetActiveRenderWindowWidget(activeRenderWindowWidget); } void QmitkMxNMultiWidget::InitializeViews(const mitk::TimeGeometry* geometry, bool resetCamera) { auto* renderingManager = mitk::RenderingManager::GetInstance(); mitk::Point3D currentPosition = mitk::Point3D(); unsigned int imageTimeStep = 0; if (!resetCamera) { // store the current position to set it again later, if the camera should not be reset currentPosition = this->GetSelectedPosition(""); // store the current time step to set it again later, if the camera should not be reset const auto currentTimePoint = renderingManager->GetTimeNavigationController()->GetSelectedTimePoint(); if (geometry->IsValidTimePoint(currentTimePoint)) { imageTimeStep = geometry->TimePointToTimeStep(currentTimePoint); } } // initialize active render window renderingManager->InitializeView( this->GetActiveRenderWindowWidget()->GetRenderWindow()->GetVtkRenderWindow(), geometry, resetCamera); if (!resetCamera) { this->SetSelectedPosition(currentPosition, ""); renderingManager->GetTimeNavigationController()->GetTime()->SetPos(imageTimeStep); } } void QmitkMxNMultiWidget::SetInteractionReferenceGeometry(const mitk::TimeGeometry* referenceGeometry) { // Set the interaction reference referenceGeometry for all render windows. auto allRenderWindows = this->GetRenderWindows(); for (auto& renderWindow : allRenderWindows) { auto* baseRenderer = mitk::BaseRenderer::GetInstance(renderWindow->GetRenderWindow()); baseRenderer->SetInteractionReferenceGeometry(referenceGeometry); } } bool QmitkMxNMultiWidget::HasCoupledRenderWindows() const { return false; } void QmitkMxNMultiWidget::SetSelectedPosition(const mitk::Point3D& newPosition, const QString& widgetName) { RenderWindowWidgetPointer renderWindowWidget; if (widgetName.isNull() || widgetName.isEmpty()) { renderWindowWidget = GetActiveRenderWindowWidget(); } else { renderWindowWidget = GetRenderWindowWidget(widgetName); } if (nullptr != renderWindowWidget) { renderWindowWidget->GetSliceNavigationController()->SelectSliceByPoint(newPosition); return; } MITK_ERROR << "Position can not be set for an unknown render window widget."; } const mitk::Point3D QmitkMxNMultiWidget::GetSelectedPosition(const QString& widgetName) const { RenderWindowWidgetPointer renderWindowWidget; if (widgetName.isNull() || widgetName.isEmpty()) { renderWindowWidget = GetActiveRenderWindowWidget(); } else { renderWindowWidget = GetRenderWindowWidget(widgetName); } if (nullptr != renderWindowWidget) { return renderWindowWidget->GetCrosshairPosition(); } MITK_ERROR << "Crosshair position can not be retrieved."; return mitk::Point3D(0.0); } void QmitkMxNMultiWidget::SetCrosshairVisibility(bool visible) { // get the specific render window that sent the signal QmitkRenderWindow* renderWindow = qobject_cast(sender()); if (nullptr == renderWindow) { return; } auto renderWindowWidget = this->GetRenderWindowWidget(renderWindow); renderWindowWidget->SetCrosshairVisibility(visible); } bool QmitkMxNMultiWidget::GetCrosshairVisibility() const { // get the specific render window that sent the signal QmitkRenderWindow* renderWindow = qobject_cast(sender()); if (nullptr == renderWindow) { return false; } auto renderWindowWidget = this->GetRenderWindowWidget(renderWindow); return renderWindowWidget->GetCrosshairVisibility(); } void QmitkMxNMultiWidget::SetCrosshairGap(unsigned int gapSize) { auto renderWindowWidgets = this->GetRenderWindowWidgets(); for (const auto& renderWindowWidget : renderWindowWidgets) { renderWindowWidget.second->SetCrosshairGap(gapSize); } } void QmitkMxNMultiWidget::ResetCrosshair() { auto dataStorage = GetDataStorage(); if (nullptr == dataStorage) { return; } // get the specific render window that sent the signal QmitkRenderWindow* renderWindow = qobject_cast(sender()); if (nullptr == renderWindow) { return; } mitk::RenderingManager::GetInstance()->InitializeViewByBoundingObjects(renderWindow->GetRenderWindow(), dataStorage); SetWidgetPlaneMode(mitk::InteractionSchemeSwitcher::MITKStandard); } void QmitkMxNMultiWidget::SetWidgetPlaneMode(int userMode) { MITK_DEBUG << "Changing crosshair mode to " << userMode; switch (userMode) { case 0: SetInteractionScheme(mitk::InteractionSchemeSwitcher::MITKStandard); break; case 1: SetInteractionScheme(mitk::InteractionSchemeSwitcher::MITKRotationUncoupled); break; case 2: SetInteractionScheme(mitk::InteractionSchemeSwitcher::MITKRotationCoupled); break; case 3: SetInteractionScheme(mitk::InteractionSchemeSwitcher::MITKSwivel); break; } } mitk::SliceNavigationController* QmitkMxNMultiWidget::GetTimeNavigationController() { return m_TimeNavigationController; } -void QmitkMxNMultiWidget::AddPlanesToDataStorage() +void QmitkMxNMultiWidget::EnableCrosshair() { auto renderWindowWidgets = this->GetRenderWindowWidgets(); for (const auto& renderWindowWidget : renderWindowWidgets) { - renderWindowWidget.second->AddPlanesToDataStorage(); + renderWindowWidget.second->EnableCrosshair(); } } -void QmitkMxNMultiWidget::RemovePlanesFromDataStorage() +void QmitkMxNMultiWidget::DisableCrosshair() { auto renderWindowWidgets = this->GetRenderWindowWidgets(); for (const auto& renderWindowWidget : renderWindowWidgets) { - renderWindowWidget.second->RemovePlanesFromDataStorage(); + renderWindowWidget.second->DisableCrosshair(); } } ////////////////////////////////////////////////////////////////////////// // PUBLIC SLOTS // MOUSE EVENTS ////////////////////////////////////////////////////////////////////////// void QmitkMxNMultiWidget::wheelEvent(QWheelEvent* e) { emit WheelMoved(e); } void QmitkMxNMultiWidget::mousePressEvent(QMouseEvent*) { // nothing here, but necessary for mouse interactions (.xml-configuration files) } void QmitkMxNMultiWidget::moveEvent(QMoveEvent* e) { QWidget::moveEvent(e); // it is necessary to readjust the position of the overlays as the MultiWidget has moved // unfortunately it's not done by QmitkRenderWindow::moveEvent -> must be done here emit Moved(); } void QmitkMxNMultiWidget::RemoveRenderWindowWidget() { auto renderWindowWidgets = this->GetRenderWindowWidgets(); auto iterator = renderWindowWidgets.find(this->GetNameFromIndex(this->GetNumberOfRenderWindowWidgets() - 1)); if (iterator == renderWindowWidgets.end()) { return; } // disconnect each signal of this render window widget RenderWindowWidgetPointer renderWindowWidgetToRemove = iterator->second; m_TimeNavigationController->Disconnect(renderWindowWidgetToRemove->GetSliceNavigationController()); QmitkAbstractMultiWidget::RemoveRenderWindowWidget(); } ////////////////////////////////////////////////////////////////////////// // PRIVATE ////////////////////////////////////////////////////////////////////////// void QmitkMxNMultiWidget::SetLayoutImpl() { int requiredRenderWindowWidgets = GetRowCount() * GetColumnCount(); int existingRenderWindowWidgets = GetRenderWindowWidgets().size(); int difference = requiredRenderWindowWidgets - existingRenderWindowWidgets; while (0 < difference) { // more render window widgets needed CreateRenderWindowWidget(); --difference; } while (0 > difference) { // less render window widgets needed RemoveRenderWindowWidget(); ++difference; } auto firstRenderWindowWidget = GetFirstRenderWindowWidget(); if (nullptr != firstRenderWindowWidget) { SetActiveRenderWindowWidget(firstRenderWindowWidget); } GetMultiWidgetLayoutManager()->SetLayoutDesign(QmitkMultiWidgetLayoutManager::LayoutDesign::DEFAULT); } QmitkAbstractMultiWidget::RenderWindowWidgetPointer QmitkMxNMultiWidget::CreateRenderWindowWidget() { // create the render window widget and connect signal / slot QString renderWindowWidgetName = GetNameFromIndex(GetNumberOfRenderWindowWidgets()); RenderWindowWidgetPointer renderWindowWidget = std::make_shared(this, renderWindowWidgetName, GetDataStorage()); renderWindowWidget->SetCornerAnnotationText(renderWindowWidgetName.toStdString()); AddRenderWindowWidget(renderWindowWidgetName, renderWindowWidget); auto renderWindow = renderWindowWidget->GetRenderWindow(); QmitkRenderWindowUtilityWidget* utilityWidget = new QmitkRenderWindowUtilityWidget(this, renderWindow, GetDataStorage()); renderWindowWidget->AddUtilityWidget(utilityWidget); connect(utilityWidget, &QmitkRenderWindowUtilityWidget::SynchronizationToggled, this, &QmitkMxNMultiWidget::ToggleSynchronization); connect(this, &QmitkMxNMultiWidget::UpdateUtilityWidgetViewPlanes, utilityWidget, &QmitkRenderWindowUtilityWidget::UpdateViewPlaneSelection); // needs to be done after 'QmitkRenderWindowUtilityWidget::ToggleSynchronization' has been connected // initially synchronize the node selection widget utilityWidget->ToggleSynchronization(true); auto layoutManager = GetMultiWidgetLayoutManager(); connect(renderWindow, &QmitkRenderWindow::LayoutDesignChanged, layoutManager, &QmitkMultiWidgetLayoutManager::SetLayoutDesign); connect(renderWindow, &QmitkRenderWindow::ResetView, this, &QmitkMxNMultiWidget::ResetCrosshair); connect(renderWindow, &QmitkRenderWindow::CrosshairVisibilityChanged, this, &QmitkMxNMultiWidget::SetCrosshairVisibility); connect(renderWindow, &QmitkRenderWindow::CrosshairRotationModeChanged, this, &QmitkMxNMultiWidget::SetWidgetPlaneMode); // connect time navigation controller to react on referenceGeometry time events with the render window's slice naviation controller m_TimeNavigationController->ConnectGeometryTimeEvent(renderWindow->GetSliceNavigationController()); // reverse connection between the render window's slice navigation controller and the time navigation controller renderWindow->GetSliceNavigationController()->ConnectGeometryTimeEvent(m_TimeNavigationController); return renderWindowWidget; } void QmitkMxNMultiWidget::LoadLayout(const nlohmann::json* jsonData) { if ((*jsonData).is_null()) { QMessageBox::warning(this, "Load layout", "Could not read window layout"); return; } unsigned int windowCounter = 0; try { auto version = jsonData->at("version").get(); if (version != "1.0") { QMessageBox::warning(this, "Load layout", "Unknown layout version, could not load"); return; } delete this->layout(); auto content = BuildLayoutFromJSON(jsonData, &windowCounter); auto hBoxLayout = new QHBoxLayout(this); this->setLayout(hBoxLayout); hBoxLayout->addWidget(content); emit UpdateUtilityWidgetViewPlanes(); } catch (nlohmann::json::out_of_range& e) { MITK_ERROR << "Error in loading window layout from JSON: " << e.what(); return; } while (GetNumberOfRenderWindowWidgets() > windowCounter) { RemoveRenderWindowWidget(); } } void QmitkMxNMultiWidget::SaveLayout(std::ostream* outStream) { if (outStream == nullptr) { return; } auto layout = this->layout(); if (layout == nullptr) return; // There should only ever be one item: a splitter auto widget = layout->itemAt(0)->widget(); auto splitter = dynamic_cast(widget); if (!splitter) { MITK_ERROR << "Tried to save unexpected layout format. Make sure the layout of this instance contains a single QSplitter."; return; } auto layoutJSON = BuildJSONFromLayout(splitter); layoutJSON["version"] = "1.0"; layoutJSON["name"] = "Custom Layout"; *outStream << std::setw(4) << layoutJSON << std::endl; } nlohmann::json QmitkMxNMultiWidget::BuildJSONFromLayout(const QSplitter* splitter) { nlohmann::json resultJSON; resultJSON["isWindow"] = false; resultJSON["vertical"] = (splitter->orientation() == Qt::Vertical) ? true : false; auto sizes = splitter->sizes(); auto content = nlohmann::json::array(); auto countSplitter = splitter->count(); for (int i = 0; i < countSplitter; ++i) { auto widget = splitter->widget(i); nlohmann::json widgetJSON; if (auto widgetSplitter = dynamic_cast(widget); widgetSplitter) { widgetJSON = BuildJSONFromLayout(widgetSplitter); } else if (auto widgetWindow = dynamic_cast(widget); widgetWindow) { widgetJSON["isWindow"] = true; widgetJSON["viewDirection"] = widgetWindow->GetSliceNavigationController()->GetViewDirectionAsString(); } widgetJSON["size"] = sizes[i]; content.push_back(widgetJSON); } resultJSON["content"] = content; return resultJSON; } QSplitter* QmitkMxNMultiWidget::BuildLayoutFromJSON(const nlohmann::json* jsonData, unsigned int* windowCounter, QSplitter* parentSplitter) { bool vertical = jsonData->at("vertical").get(); auto orientation = vertical ? Qt::Vertical : Qt::Horizontal; auto split = new QSplitter(orientation, parentSplitter); QList sizes; for (auto object : jsonData->at("content")) { bool isWindow = object["isWindow"].get(); int size = object["size"].get(); sizes.append(size); if (isWindow) { auto viewDirection = object["viewDirection"].get(); mitk::AnatomicalPlane viewPlane = mitk::AnatomicalPlane::Sagittal; if (viewDirection == "Axial") { viewPlane = mitk::AnatomicalPlane::Axial; } else if (viewDirection == "Coronal") { viewPlane = mitk::AnatomicalPlane::Coronal; } else if (viewDirection == "Original") { viewPlane = mitk::AnatomicalPlane::Original; } else if (viewDirection == "Sagittal") { viewPlane = mitk::AnatomicalPlane::Sagittal; } QmitkAbstractMultiWidget::RenderWindowWidgetPointer window = nullptr; QString renderWindowName; QmitkAbstractMultiWidget::RenderWindowWidgetMap::iterator it; // repurpose existing render windows as far as they already exist if (*windowCounter < GetRenderWindowWidgets().size()) { renderWindowName = this->GetNameFromIndex(*windowCounter); auto renderWindowWidgets = GetRenderWindowWidgets(); it = renderWindowWidgets.find(renderWindowName); if (it != renderWindowWidgets.end()) { window = it->second; } else { MITK_ERROR << "Could not find render window " << renderWindowName.toStdString() << ", although it should be there."; } } if (window == nullptr) { window = CreateRenderWindowWidget(); } window->GetSliceNavigationController()->SetDefaultViewDirection(viewPlane); window->GetSliceNavigationController()->Update(); split->addWidget(window.get()); window->show(); (*windowCounter)++; } else { auto subSplitter = BuildLayoutFromJSON(&object, windowCounter, split); split->addWidget(subSplitter); } } split->setSizes(sizes); return split; } void QmitkMxNMultiWidget::SetInitialSelection() { auto dataStorage = this->GetDataStorage(); if (nullptr == dataStorage) { return; } mitk::NodePredicateAnd::Pointer noHelperObjects = mitk::NodePredicateAnd::New(); noHelperObjects->AddPredicate(mitk::NodePredicateNot::New(mitk::NodePredicateProperty::New("helper object"))); noHelperObjects->AddPredicate(mitk::NodePredicateNot::New(mitk::NodePredicateProperty::New("hidden object"))); auto allNodes = dataStorage->GetSubset(noHelperObjects); QmitkSynchronizedNodeSelectionWidget::NodeList currentSelection; for (auto& node : *allNodes) { currentSelection.append(node); } m_SynchronizedWidgetConnector->ChangeSelection(currentSelection); } void QmitkMxNMultiWidget::ToggleSynchronization(QmitkSynchronizedNodeSelectionWidget* synchronizedWidget) { bool synchronized = synchronizedWidget->IsSynchronized(); if (synchronized) { m_SynchronizedWidgetConnector->ConnectWidget(synchronizedWidget); m_SynchronizedWidgetConnector->SynchronizeWidget(synchronizedWidget); } else { m_SynchronizedWidgetConnector->DisconnectWidget(synchronizedWidget); } } diff --git a/Modules/QtWidgets/src/QmitkRenderWindowWidget.cpp b/Modules/QtWidgets/src/QmitkRenderWindowWidget.cpp index 20df5b5f9d..2704321fac 100644 --- a/Modules/QtWidgets/src/QmitkRenderWindowWidget.cpp +++ b/Modules/QtWidgets/src/QmitkRenderWindowWidget.cpp @@ -1,319 +1,316 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #include "QmitkRenderWindowWidget.h" // vtk #include #include QmitkRenderWindowWidget::QmitkRenderWindowWidget(QWidget* parent/* = nullptr*/, const QString& widgetName/* = ""*/, mitk::DataStorage* dataStorage/* = nullptr*/) : QFrame(parent) , m_WidgetName(widgetName) , m_DataStorage(dataStorage) , m_RenderWindow(nullptr) , m_CrosshairManager(nullptr) { this->InitializeGUI(); } QmitkRenderWindowWidget::~QmitkRenderWindowWidget() { auto sliceNavigationController = this->GetSliceNavigationController(); if (nullptr != sliceNavigationController) { sliceNavigationController->SetCrosshairEvent.RemoveListener( mitk::MessageDelegate1( this, &QmitkRenderWindowWidget::SetCrosshairPosition)); } } void QmitkRenderWindowWidget::SetDataStorage(mitk::DataStorage* dataStorage) { if (dataStorage == m_DataStorage) { return; } m_DataStorage = dataStorage; if (nullptr != m_RenderWindow) { mitk::BaseRenderer::GetInstance(m_RenderWindow->renderWindow())->SetDataStorage(dataStorage); } - - m_CrosshairManager->SetDataStorage(m_DataStorage); } mitk::SliceNavigationController* QmitkRenderWindowWidget::GetSliceNavigationController() const { return m_RenderWindow->GetSliceNavigationController(); } void QmitkRenderWindowWidget::RequestUpdate() { mitk::RenderingManager::GetInstance()->RequestUpdate(m_RenderWindow->renderWindow()); } void QmitkRenderWindowWidget::ForceImmediateUpdate() { mitk::RenderingManager::GetInstance()->ForceImmediateUpdate(m_RenderWindow->renderWindow()); } void QmitkRenderWindowWidget::AddUtilityWidget(QWidget* utilityWidget) { m_Layout->insertWidget(0, utilityWidget); } void QmitkRenderWindowWidget::SetGradientBackgroundColors(const mitk::Color& upper, const mitk::Color& lower) { vtkRenderer* vtkRenderer = m_RenderWindow->GetRenderer()->GetVtkRenderer(); if (nullptr == vtkRenderer) { return; } m_GradientBackgroundColors.first = upper; m_GradientBackgroundColors.second = lower; vtkRenderer->SetBackground(lower[0], lower[1], lower[2]); vtkRenderer->SetBackground2(upper[0], upper[1], upper[2]); ShowGradientBackground(true); } void QmitkRenderWindowWidget::ShowGradientBackground(bool show) { m_RenderWindow->GetRenderer()->GetVtkRenderer()->SetGradientBackground(show); } bool QmitkRenderWindowWidget::IsGradientBackgroundOn() const { return m_RenderWindow->GetRenderer()->GetVtkRenderer()->GetGradientBackground(); } void QmitkRenderWindowWidget::SetDecorationColor(const mitk::Color& color) { m_DecorationColor = color; m_CornerAnnotation->GetTextProperty()->SetColor(m_DecorationColor[0], m_DecorationColor[1], m_DecorationColor[2]); QColor hexColor(m_DecorationColor[0] * 255, m_DecorationColor[1] * 255, m_DecorationColor[2] * 255); setStyleSheet("QmitkRenderWindowWidget { border: 2px solid " + hexColor.name(QColor::HexRgb) + "; }"); } void QmitkRenderWindowWidget::ShowColoredRectangle(bool show) { if (show) { setFrameStyle(QFrame::Box | QFrame::Plain); } else { setFrameStyle(NoFrame); } } bool QmitkRenderWindowWidget::IsColoredRectangleVisible() const { return frameStyle() > 0; } void QmitkRenderWindowWidget::ShowCornerAnnotation(bool show) { m_CornerAnnotation->SetVisibility(show); } bool QmitkRenderWindowWidget::IsCornerAnnotationVisible() const { return m_CornerAnnotation->GetVisibility() > 0; } void QmitkRenderWindowWidget::SetCornerAnnotationText(const std::string& cornerAnnotation) { m_CornerAnnotation->SetText(0, cornerAnnotation.c_str()); } std::string QmitkRenderWindowWidget::GetCornerAnnotationText() const { return std::string(m_CornerAnnotation->GetText(0)); } bool QmitkRenderWindowWidget::IsRenderWindowMenuActivated() const { return m_RenderWindow->GetActivateMenuWidgetFlag(); } void QmitkRenderWindowWidget::SetCrosshairVisibility(bool visible) { - m_CrosshairManager->SetCrosshairVisibility(visible); + m_CrosshairManager->SetCrosshairVisibility(visible, m_RenderWindow->GetRenderer()); this->RequestUpdate(); } bool QmitkRenderWindowWidget::GetCrosshairVisibility() { - return m_CrosshairManager->GetCrosshairVisibility(); + return m_CrosshairManager->GetCrosshairVisibility(m_RenderWindow->GetRenderer()); } void QmitkRenderWindowWidget::SetCrosshairGap(unsigned int gapSize) { m_CrosshairManager->SetCrosshairGap(gapSize); } -void QmitkRenderWindowWidget::AddPlanesToDataStorage() +void QmitkRenderWindowWidget::EnableCrosshair() { - m_CrosshairManager->AddPlanesToDataStorage(); + m_CrosshairManager->AddCrosshairNodeToDataStorage(m_DataStorage); } -void QmitkRenderWindowWidget::RemovePlanesFromDataStorage() +void QmitkRenderWindowWidget::DisableCrosshair() { - m_CrosshairManager->RemovePlanesFromDataStorage(); + m_CrosshairManager->RemoveCrosshairNodeFromDataStorage(m_DataStorage); } void QmitkRenderWindowWidget::InitializeGUI() { m_Layout = new QVBoxLayout(this); m_Layout->setMargin(0); setLayout(m_Layout); setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); setContentsMargins(0, 0, 0, 0); if (nullptr == m_DataStorage) { return; } mitk::RenderingManager::GetInstance()->SetDataStorage(m_DataStorage); // create render window for this render window widget m_RenderWindow = new QmitkRenderWindow(this, m_WidgetName, nullptr); m_RenderWindow->SetLayoutIndex(mitk::AnatomicalPlane::Sagittal); connect(m_RenderWindow, &QmitkRenderWindow::ResetGeometry, this, &QmitkRenderWindowWidget::OnResetGeometry); - auto sliceNavigationController = this->GetSliceNavigationController(); + auto* sliceNavigationController = this->GetSliceNavigationController(); sliceNavigationController->SetDefaultViewDirection(mitk::AnatomicalPlane::Sagittal); m_Layout->addWidget(m_RenderWindow); // set colors and corner annotation InitializeDecorations(); // use crosshair manager - m_CrosshairManager = mitk::CrosshairManager::New(m_DataStorage, m_RenderWindow->GetRenderer()); + m_CrosshairManager = mitk::CrosshairManager::New(m_RenderWindow->GetRenderer()); sliceNavigationController->SetCrosshairEvent.AddListener( mitk::MessageDelegate1( this, &QmitkRenderWindowWidget::SetCrosshairPosition)); // finally add observer, after all relevant objects have been created / initialized sliceNavigationController->ConnectGeometrySendEvent(this); sliceNavigationController->ConnectGeometrySliceEvent(this); mitk::TimeGeometry::ConstPointer timeGeometry = m_DataStorage->ComputeBoundingGeometry3D(m_DataStorage->GetAll()); mitk::RenderingManager::GetInstance()->InitializeView(m_RenderWindow->GetVtkRenderWindow(), timeGeometry); } void QmitkRenderWindowWidget::InitializeDecorations() { vtkRenderer* vtkRenderer = m_RenderWindow->GetRenderer()->GetVtkRenderer(); if (nullptr == vtkRenderer) { return; } // initialize background color gradients float black[3] = { 0.0f, 0.0f, 0.0f }; SetGradientBackgroundColors(black, black); // initialize annotation text and decoration color setFrameStyle(QFrame::Box | QFrame::Plain); m_CornerAnnotation = vtkSmartPointer::New(); m_CornerAnnotation->SetText(0, "Sagittal"); m_CornerAnnotation->SetMaximumFontSize(12); if (0 == vtkRenderer->HasViewProp(m_CornerAnnotation)) { vtkRenderer->AddViewProp(m_CornerAnnotation); } float white[3] = { 1.0f, 1.0f, 1.0f }; SetDecorationColor(mitk::Color(white)); } void QmitkRenderWindowWidget::SetCrosshairPosition(const mitk::Point3D& newPosition) { m_CrosshairManager->SetCrosshairPosition(newPosition); this->RequestUpdate(); } mitk::Point3D QmitkRenderWindowWidget::GetCrosshairPosition() const { return m_CrosshairManager->GetCrosshairPosition(); } void QmitkRenderWindowWidget::SetGeometry(const itk::EventObject& event) { if (!mitk::SliceNavigationController::GeometrySendEvent(nullptr, 0).CheckEvent(&event)) { return; } - auto sliceNavigationController = this->GetSliceNavigationController(); - const auto* inputTimeGeometry = sliceNavigationController->GetInputWorldTimeGeometry(); - m_CrosshairManager->ComputeOrientedTimeGeometries(inputTimeGeometry); + const auto* sliceNavigationController = this->GetSliceNavigationController(); + m_CrosshairManager->UpdateCrosshairPosition(sliceNavigationController); } void QmitkRenderWindowWidget::SetGeometrySlice(const itk::EventObject& event) { if (!mitk::SliceNavigationController::GeometrySliceEvent(nullptr, 0).CheckEvent(&event)) { return; } - auto sliceNavigationController = this->GetSliceNavigationController(); - m_CrosshairManager->UpdateSlice(sliceNavigationController); + const auto* sliceNavigationController = this->GetSliceNavigationController(); + m_CrosshairManager->UpdateCrosshairPosition(sliceNavigationController); } void QmitkRenderWindowWidget::OnResetGeometry() { - auto* baseRenderer = mitk::BaseRenderer::GetInstance(m_RenderWindow->GetRenderWindow()); + const auto* baseRenderer = mitk::BaseRenderer::GetInstance(m_RenderWindow->GetRenderWindow()); const auto* interactionReferenceGeometry = baseRenderer->GetInteractionReferenceGeometry(); this->ResetGeometry(interactionReferenceGeometry); m_RenderWindow->ShowOverlayMessage(false); } void QmitkRenderWindowWidget::ResetGeometry(const mitk::TimeGeometry* referenceGeometry) { if (nullptr == referenceGeometry) { return; } mitk::TimeStepType imageTimeStep = 0; // store the current position to set it again later, if the camera should not be reset mitk::Point3D currentPosition = this->GetCrosshairPosition(); // store the current time step to set it again later, if the camera should not be reset auto* renderingManager = mitk::RenderingManager::GetInstance(); const auto currentTimePoint = renderingManager->GetTimeNavigationController()->GetSelectedTimePoint(); if (referenceGeometry->IsValidTimePoint(currentTimePoint)) { imageTimeStep = referenceGeometry->TimePointToTimeStep(currentTimePoint); } - auto* baseRenderer = mitk::BaseRenderer::GetInstance(m_RenderWindow->renderWindow()); + const auto* baseRenderer = mitk::BaseRenderer::GetInstance(m_RenderWindow->renderWindow()); renderingManager->InitializeView(baseRenderer->GetRenderWindow(), referenceGeometry, false); // reset position and time step this->GetSliceNavigationController()->SelectSliceByPoint(currentPosition); renderingManager->GetTimeNavigationController()->GetTime()->SetPos(imageTimeStep); } diff --git a/Plugins/org.mitk.gui.qt.mxnmultiwidgeteditor/src/QmitkMxNMultiWidgetEditor.cpp b/Plugins/org.mitk.gui.qt.mxnmultiwidgeteditor/src/QmitkMxNMultiWidgetEditor.cpp index ff93413c38..bc536b0f6a 100644 --- a/Plugins/org.mitk.gui.qt.mxnmultiwidgeteditor/src/QmitkMxNMultiWidgetEditor.cpp +++ b/Plugins/org.mitk.gui.qt.mxnmultiwidgeteditor/src/QmitkMxNMultiWidgetEditor.cpp @@ -1,238 +1,238 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #include "QmitkMxNMultiWidgetEditor.h" #include #include #include #include #include #include // mxn multi widget editor plugin #include "QmitkMultiWidgetDecorationManager.h" // mitk qt widgets module #include #include #include // qt #include const QString QmitkMxNMultiWidgetEditor::EDITOR_ID = "org.mitk.editors.mxnmultiwidget"; struct QmitkMxNMultiWidgetEditor::Impl final { Impl(); ~Impl() = default; QmitkInteractionSchemeToolBar* m_InteractionSchemeToolBar; QmitkMultiWidgetConfigurationToolBar* m_ConfigurationToolBar; }; QmitkMxNMultiWidgetEditor::Impl::Impl() : m_InteractionSchemeToolBar(nullptr) , m_ConfigurationToolBar(nullptr) { // nothing here } ////////////////////////////////////////////////////////////////////////// // QmitkMxNMultiWidgetEditor ////////////////////////////////////////////////////////////////////////// QmitkMxNMultiWidgetEditor::QmitkMxNMultiWidgetEditor() : QmitkAbstractMultiWidgetEditor() , m_Impl(std::make_unique()) { // nothing here } QmitkMxNMultiWidgetEditor::~QmitkMxNMultiWidgetEditor() { GetSite()->GetPage()->RemovePartListener(this); } berry::IPartListener::Events::Types QmitkMxNMultiWidgetEditor::GetPartEventTypes() const { return Events::CLOSED | Events::OPENED | Events::HIDDEN | Events::VISIBLE; } void QmitkMxNMultiWidgetEditor::PartClosed(const berry::IWorkbenchPartReference::Pointer& partRef) { if (partRef->GetId() == QmitkMxNMultiWidgetEditor::EDITOR_ID) { const auto& multiWidget = dynamic_cast(GetMultiWidget()); if (nullptr != multiWidget) { - multiWidget->RemovePlanesFromDataStorage(); + multiWidget->DisableCrosshair(); multiWidget->ActivateMenuWidget(false); } } } void QmitkMxNMultiWidgetEditor::PartOpened(const berry::IWorkbenchPartReference::Pointer& partRef) { if (partRef->GetId() == QmitkMxNMultiWidgetEditor::EDITOR_ID) { const auto& multiWidget = dynamic_cast(GetMultiWidget()); if (nullptr != multiWidget) { - multiWidget->AddPlanesToDataStorage(); + multiWidget->EnableCrosshair(); multiWidget->ActivateMenuWidget(true); } } } void QmitkMxNMultiWidgetEditor::PartHidden(const berry::IWorkbenchPartReference::Pointer& partRef) { if (partRef->GetId() == QmitkMxNMultiWidgetEditor::EDITOR_ID) { const auto& multiWidget = dynamic_cast(GetMultiWidget()); if (nullptr != multiWidget) { multiWidget->ActivateMenuWidget(false); } } } void QmitkMxNMultiWidgetEditor::PartVisible(const berry::IWorkbenchPartReference::Pointer& partRef) { if (partRef->GetId() == QmitkMxNMultiWidgetEditor::EDITOR_ID) { const auto& multiWidget = dynamic_cast(GetMultiWidget()); if (nullptr != multiWidget) { multiWidget->ActivateMenuWidget(true); } } } void QmitkMxNMultiWidgetEditor::OnLayoutSet(int row, int column) { const auto &multiWidget = dynamic_cast(GetMultiWidget()); if (nullptr != multiWidget) { QmitkAbstractMultiWidgetEditor::OnLayoutSet(row, column); - multiWidget->AddPlanesToDataStorage(); + multiWidget->EnableCrosshair(); } } void QmitkMxNMultiWidgetEditor::OnInteractionSchemeChanged(mitk::InteractionSchemeSwitcher::InteractionScheme scheme) { const auto &multiWidget = GetMultiWidget(); if (nullptr == multiWidget) { return; } if (mitk::InteractionSchemeSwitcher::PACSStandard == scheme) { m_Impl->m_InteractionSchemeToolBar->setVisible(true); } else { m_Impl->m_InteractionSchemeToolBar->setVisible(false); } QmitkAbstractMultiWidgetEditor::OnInteractionSchemeChanged(scheme); } ////////////////////////////////////////////////////////////////////////// // PRIVATE ////////////////////////////////////////////////////////////////////////// void QmitkMxNMultiWidgetEditor::SetFocus() { const auto& multiWidget = GetMultiWidget(); if (nullptr != multiWidget) { multiWidget->setFocus(); } } void QmitkMxNMultiWidgetEditor::CreateQtPartControl(QWidget* parent) { QHBoxLayout *layout = new QHBoxLayout(parent); layout->setContentsMargins(0, 0, 0, 0); auto* preferences = this->GetPreferences(); auto multiWidget = GetMultiWidget(); if (nullptr == multiWidget) { multiWidget = new QmitkMxNMultiWidget(parent, 0, nullptr); // create left toolbar: interaction scheme toolbar to switch how the render window navigation behaves in PACS mode if (nullptr == m_Impl->m_InteractionSchemeToolBar) { m_Impl->m_InteractionSchemeToolBar = new QmitkInteractionSchemeToolBar(parent); layout->addWidget(m_Impl->m_InteractionSchemeToolBar); } m_Impl->m_InteractionSchemeToolBar->SetInteractionEventHandler(multiWidget->GetInteractionEventHandler()); multiWidget->SetDataStorage(GetDataStorage()); multiWidget->InitializeMultiWidget(); SetMultiWidget(multiWidget); } layout->addWidget(multiWidget); // create right toolbar: configuration toolbar to change the render window widget layout if (nullptr == m_Impl->m_ConfigurationToolBar) { m_Impl->m_ConfigurationToolBar = new QmitkMultiWidgetConfigurationToolBar(multiWidget); layout->addWidget(m_Impl->m_ConfigurationToolBar); } connect(m_Impl->m_ConfigurationToolBar, &QmitkMultiWidgetConfigurationToolBar::LayoutSet, this, &QmitkMxNMultiWidgetEditor::OnLayoutSet); connect(m_Impl->m_ConfigurationToolBar, &QmitkMultiWidgetConfigurationToolBar::Synchronized, this, &QmitkMxNMultiWidgetEditor::OnSynchronize); connect(m_Impl->m_ConfigurationToolBar, &QmitkMultiWidgetConfigurationToolBar::InteractionSchemeChanged, this, &QmitkMxNMultiWidgetEditor::OnInteractionSchemeChanged); connect(m_Impl->m_ConfigurationToolBar, &QmitkMultiWidgetConfigurationToolBar::SaveLayout, static_cast(GetMultiWidget()), &QmitkMxNMultiWidget::SaveLayout, Qt::DirectConnection); connect(m_Impl->m_ConfigurationToolBar, &QmitkMultiWidgetConfigurationToolBar::LoadLayout, static_cast(GetMultiWidget()), &QmitkMxNMultiWidget::LoadLayout); GetSite()->GetPage()->AddPartListener(this); OnPreferencesChanged(preferences); } void QmitkMxNMultiWidgetEditor::OnPreferencesChanged(const mitk::IPreferences* preferences) { const auto& multiWidget = GetMultiWidget(); if (nullptr == multiWidget) { return; } // update decoration preferences //m_Impl->m_MultiWidgetDecorationManager->DecorationPreferencesChanged(preferences); int crosshairGapSize = preferences->GetInt("crosshair gap size", 32); multiWidget->SetCrosshairGap(crosshairGapSize); // zooming and panning preferences bool constrainedZooming = preferences->GetBool("Use constrained zooming and panning", true); mitk::RenderingManager::GetInstance()->SetConstrainedPanningZooming(constrainedZooming); bool PACSInteractionScheme = preferences->GetBool("PACS like mouse interaction", false); OnInteractionSchemeChanged(PACSInteractionScheme ? mitk::InteractionSchemeSwitcher::PACSStandard : mitk::InteractionSchemeSwitcher::MITKStandard); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); }