diff --git a/Plugins/org.mitk.gui.qt.common/src/QmitkAbstractView.cpp b/Plugins/org.mitk.gui.qt.common/src/QmitkAbstractView.cpp index e333ed3532..7639cb1f42 100644 --- a/Plugins/org.mitk.gui.qt.common/src/QmitkAbstractView.cpp +++ b/Plugins/org.mitk.gui.qt.common/src/QmitkAbstractView.cpp @@ -1,522 +1,563 @@ /*============================================================================ 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 "QmitkAbstractView.h" #include "QmitkDataNodeSelectionProvider.h" #include "internal/QmitkCommonActivator.h" #include "internal/QmitkDataNodeItemModel.h" // mitk Includes #include #include #include #include #include // berry Includes #include #include #include #include #include // CTK Includes #include // Qt Includes #include #include #include #include #include class QmitkAbstractViewPrivate { public: QmitkAbstractViewPrivate(QmitkAbstractView* qq) : q(qq) , m_PrefServiceTracker(QmitkCommonActivator::GetContext()) , m_DataStorageServiceTracker(QmitkCommonActivator::GetContext()) , m_Parent(nullptr) , m_DataNodeItemModel(new QmitkDataNodeItemModel) , m_DataNodeSelectionModel(new QItemSelectionModel(m_DataNodeItemModel)) , m_InDataStorageChanged(false) { m_PrefServiceTracker.open(); m_DataStorageServiceTracker.open(); } ~QmitkAbstractViewPrivate() { delete m_DataNodeSelectionModel; delete m_DataNodeItemModel; m_PrefServiceTracker.close(); m_DataStorageServiceTracker.close(); } /** * Called when a DataStorage Add Event was thrown. Sets * m_InDataStorageChanged to true and calls NodeAdded afterwards. * \see m_InDataStorageChanged */ void NodeAddedProxy(const mitk::DataNode* node) { // garantuee no recursions when a new node event is thrown in NodeAdded() if(!m_InDataStorageChanged) { m_InDataStorageChanged = true; q->NodeAdded(node); q->DataStorageModified(); m_InDataStorageChanged = false; } } /** * Called when a DataStorage remove event was thrown. Sets * m_InDataStorageChanged to true and calls NodeRemoved afterwards. * \see m_InDataStorageChanged */ void NodeRemovedProxy(const mitk::DataNode* node) { // garantuee no recursions when a new node event is thrown in NodeAdded() if(!m_InDataStorageChanged) { m_InDataStorageChanged = true; q->NodeRemoved(node); q->DataStorageModified(); m_InDataStorageChanged = false; } } /** * Called when a DataStorage changed event was thrown. Sets * m_InDataStorageChanged to true and calls NodeChanged afterwards. * \see m_InDataStorageChanged */ void NodeChangedProxy(const mitk::DataNode* node) { // garantuee no recursions when a new node event is thrown in NodeAdded() if(!m_InDataStorageChanged) { m_InDataStorageChanged = true; q->NodeChanged(node); q->DataStorageModified(); m_InDataStorageChanged = false; } } /** * reactions to selection events from views */ void BlueBerrySelectionChanged(const berry::IWorkbenchPart::Pointer& sourcepart, const berry::ISelection::ConstPointer& selection) { if(sourcepart.IsNull() || sourcepart.GetPointer() == static_cast(q)) return; if(selection.IsNull()) { q->OnNullSelection(sourcepart); return; } mitk::DataNodeSelection::ConstPointer _DataNodeSelection = selection.Cast(); q->OnSelectionChanged(sourcepart, this->DataNodeSelectionToQList(_DataNodeSelection)); } /** * Converts a mitk::DataNodeSelection to a QList (possibly empty) */ QList DataNodeSelectionToQList(mitk::DataNodeSelection::ConstPointer currentSelection) const; QmitkAbstractView* const q; ctkServiceTracker m_PrefServiceTracker; ctkServiceTracker m_DataStorageServiceTracker; /** * Saves the parent of this view (this is the scrollarea created in CreatePartControl(QWidget*) * \see CreatePartControl(QWidget*) */ QWidget* m_Parent; /** * Holds the current selection (selection made by this View !!!) */ QmitkDataNodeSelectionProvider::Pointer m_SelectionProvider; /** * Holds a helper model for firing selection events. */ QmitkDataNodeItemModel* m_DataNodeItemModel; /** * The selection model for the QmitkDataNodeItemModel; */ QItemSelectionModel* m_DataNodeSelectionModel; /** * object to observe BlueBerry selections */ QScopedPointer m_BlueBerrySelectionListener; /** * Saves if this class is currently working on DataStorage changes. * This is a protector variable to avoid recursive calls on event listener functions. */ bool m_InDataStorageChanged; }; QmitkAbstractView::QmitkAbstractView() : d(new QmitkAbstractViewPrivate(this)) { } void QmitkAbstractView::CreatePartControl(QWidget* parent) { // scrollArea auto scrollArea = new QScrollArea; //QVBoxLayout* scrollAreaLayout = new QVBoxLayout(scrollArea); scrollArea->setFrameShadow(QFrame::Plain); scrollArea->setFrameShape(QFrame::NoFrame); scrollArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAsNeeded); scrollArea->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded); // m_Parent d->m_Parent = new QWidget; //m_Parent->setSizePolicy(QSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding)); this->CreateQtPartControl(d->m_Parent); //scrollAreaLayout->addWidget(m_Parent); //scrollArea->setLayout(scrollAreaLayout); // set the widget now scrollArea->setWidgetResizable(true); scrollArea->setWidget(d->m_Parent); // add the scroll area to the real parent (the view tabbar) QWidget* parentQWidget = static_cast(parent); auto parentLayout = new QVBoxLayout(parentQWidget); parentLayout->setMargin(0); parentLayout->setSpacing(0); parentLayout->addWidget(scrollArea); // finally set the layout containing the scroll area to the parent widget (= show it) parentQWidget->setLayout(parentLayout); this->AfterCreateQtPartControl(); } void QmitkAbstractView::AfterCreateQtPartControl() { this->SetSelectionProvider(); // REGISTER DATASTORAGE LISTENER this->GetDataStorage()->AddNodeEvent.AddListener( mitk::MessageDelegate1 ( d.data(), &QmitkAbstractViewPrivate::NodeAddedProxy ) ); this->GetDataStorage()->ChangedNodeEvent.AddListener( mitk::MessageDelegate1 ( d.data(), &QmitkAbstractViewPrivate::NodeChangedProxy ) ); this->GetDataStorage()->RemoveNodeEvent.AddListener( mitk::MessageDelegate1 ( d.data(), &QmitkAbstractViewPrivate::NodeRemovedProxy ) ); // REGISTER PREFERENCES LISTENER berry::IBerryPreferences::Pointer prefs = this->GetPreferences().Cast(); if(prefs.IsNotNull()) prefs->OnChanged.AddListener( berry::MessageDelegate1(this, &QmitkAbstractView::OnPreferencesChanged)); // REGISTER FOR WORKBENCH SELECTION EVENTS d->m_BlueBerrySelectionListener.reset(new berry::NullSelectionChangedAdapter( d.data(), &QmitkAbstractViewPrivate::BlueBerrySelectionChanged)); this->GetSite()->GetWorkbenchWindow()->GetSelectionService()->AddPostSelectionListener(d->m_BlueBerrySelectionListener.data()); // EMULATE INITIAL SELECTION EVENTS // send the current selection berry::IWorkbenchPart::Pointer activePart = this->GetSite()->GetPage()->GetActivePart(); if (activePart.IsNotNull()) { this->OnSelectionChanged(activePart, this->GetCurrentSelection()); } // send preferences changed event this->OnPreferencesChanged(this->GetPreferences().Cast().GetPointer()); } QmitkAbstractView::~QmitkAbstractView() { this->Register(); this->GetDataStorage()->AddNodeEvent.RemoveListener( mitk::MessageDelegate1 ( d.data(), &QmitkAbstractViewPrivate::NodeAddedProxy ) ); this->GetDataStorage()->RemoveNodeEvent.RemoveListener( mitk::MessageDelegate1 ( d.data(), &QmitkAbstractViewPrivate::NodeRemovedProxy) ); this->GetDataStorage()->ChangedNodeEvent.RemoveListener( mitk::MessageDelegate1 ( d.data(), &QmitkAbstractViewPrivate::NodeChangedProxy ) ); berry::IBerryPreferences::Pointer prefs = this->GetPreferences().Cast(); if(prefs.IsNotNull()) { prefs->OnChanged.RemoveListener( berry::MessageDelegate1(this, &QmitkAbstractView::OnPreferencesChanged)); // flush the preferences here (disabled, everyone should flush them by themselves at the right moment) // prefs->Flush(); } // REMOVE SELECTION PROVIDER this->GetSite()->SetSelectionProvider(berry::ISelectionProvider::Pointer(nullptr)); berry::ISelectionService* s = GetSite()->GetWorkbenchWindow()->GetSelectionService(); if(s) { s->RemovePostSelectionListener(d->m_BlueBerrySelectionListener.data()); } this->UnRegister(false); } void QmitkAbstractView::SetSelectionProvider() { // REGISTER A SELECTION PROVIDER d->m_SelectionProvider = QmitkDataNodeSelectionProvider::Pointer(new QmitkDataNodeSelectionProvider); d->m_SelectionProvider->SetItemSelectionModel(GetDataNodeSelectionModel()); this->GetSite()->SetSelectionProvider(berry::ISelectionProvider::Pointer(d->m_SelectionProvider)); } QItemSelectionModel *QmitkAbstractView::GetDataNodeSelectionModel() const { return nullptr; } void QmitkAbstractView::OnPreferencesChanged( const berry::IBerryPreferences* ) { } void QmitkAbstractView::DataStorageModified() { } void QmitkAbstractView::DataStorageChanged(mitk::IDataStorageReference::Pointer /*dsRef*/) { } mitk::IRenderWindowPart* QmitkAbstractView::GetRenderWindowPart(mitk::WorkbenchUtil::IRenderWindowPartStrategies strategies) const { berry::IWorkbenchPage::Pointer page = GetSite()->GetPage(); return mitk::WorkbenchUtil::GetRenderWindowPart(page, strategies); } void QmitkAbstractView::RequestRenderWindowUpdate(mitk::RenderingManager::RequestType requestType) { mitk::IRenderWindowPart* renderPart = this->GetRenderWindowPart(); - if (renderPart == nullptr) return; + if (renderPart == nullptr) + return; if (mitk::IRenderingManager* renderingManager = renderPart->GetRenderingManager()) { renderingManager->RequestUpdateAll(requestType); } else { renderPart->RequestUpdate(requestType); } } +void QmitkAbstractView::InitializeRenderWindows(const mitk::TimeGeometry *referenceGeometry, + mitk::RenderingManager::RequestType requestType, + bool resetCamera) +{ + mitk::IRenderWindowPart* renderWindowPart = this->GetRenderWindowPart(); + if (nullptr == renderWindowPart) + { + return; + } + + mitk::IRenderingManager* renderingManager = renderWindowPart->GetRenderingManager(); + if (nullptr == renderingManager) + { + return; + } + + 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 = renderWindowPart->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 (referenceGeometry->IsValidTimePoint(currentTimePoint)) + { + imageTimeStep = referenceGeometry->TimePointToTimeStep(currentTimePoint); + } + } + + // initialize render windows + renderingManager->InitializeViews(referenceGeometry, requestType, resetCamera); + + if (!resetCamera) + { + renderWindowPart->SetSelectedPosition(currentPosition); + renderingManager->GetTimeNavigationController()->GetTime()->SetPos(imageTimeStep); + } +} + void QmitkAbstractView::HandleException( const char* str, QWidget* parent, bool showDialog ) const { //itkGenericOutputMacro( << "Exception caught: " << str ); MITK_ERROR << str; if ( showDialog ) { QMessageBox::critical ( parent, "Exception caught!", str ); } } void QmitkAbstractView::HandleException( std::exception& e, QWidget* parent, bool showDialog ) const { HandleException( e.what(), parent, showDialog ); } void QmitkAbstractView::WaitCursorOn() { QApplication::setOverrideCursor( QCursor(Qt::WaitCursor) ); } void QmitkAbstractView::BusyCursorOn() { QApplication::setOverrideCursor( QCursor(Qt::BusyCursor) ); } void QmitkAbstractView::WaitCursorOff() { this->RestoreOverrideCursor(); } void QmitkAbstractView::BusyCursorOff() { this->RestoreOverrideCursor(); } void QmitkAbstractView::RestoreOverrideCursor() { QApplication::restoreOverrideCursor(); } berry::IPreferences::Pointer QmitkAbstractView::GetPreferences() const { berry::IPreferencesService* prefService = d->m_PrefServiceTracker.getService(); // const_cast workaround for bad programming: const uncorrectness this->GetViewSite() should be const QString id = "/" + (const_cast(this))->GetViewSite()->GetId(); return prefService ? prefService->GetSystemPreferences()->Node(id): berry::IPreferences::Pointer(nullptr); } -mitk::DataStorage::Pointer -QmitkAbstractView::GetDataStorage() const +mitk::DataStorage::Pointer QmitkAbstractView::GetDataStorage() const { mitk::IDataStorageService* dsService = d->m_DataStorageServiceTracker.getService(); if (dsService != nullptr) { return dsService->GetDataStorage()->GetDataStorage(); } return nullptr; } mitk::IDataStorageReference::Pointer QmitkAbstractView::GetDataStorageReference() const { mitk::IDataStorageService* dsService = d->m_DataStorageServiceTracker.getService(); if (dsService != nullptr) { return dsService->GetDataStorage(); } return mitk::IDataStorageReference::Pointer(nullptr); } QList QmitkAbstractView::GetCurrentSelection() const { berry::ISelection::ConstPointer selection( this->GetSite()->GetWorkbenchWindow()->GetSelectionService()->GetSelection()); mitk::DataNodeSelection::ConstPointer currentSelection = selection.Cast(); return d->DataNodeSelectionToQList(currentSelection); } bool QmitkAbstractView::IsCurrentSelectionValid() const { return this->GetSite()->GetWorkbenchWindow()->GetSelectionService()->GetSelection(); } QList QmitkAbstractView::GetDataManagerSelection() const { berry::ISelection::ConstPointer selection( this->GetSite()->GetWorkbenchWindow()->GetSelectionService()->GetSelection("org.mitk.views.datamanager")); mitk::DataNodeSelection::ConstPointer currentSelection = selection.Cast(); return d->DataNodeSelectionToQList(currentSelection); } bool QmitkAbstractView::IsDataManagerSelectionValid() const { return this->GetSite()->GetWorkbenchWindow()->GetSelectionService()->GetSelection("org.mitk.views.datamanager"); } void QmitkAbstractView::SetDataManagerSelection(const berry::ISelection::ConstPointer &selection, QItemSelectionModel::SelectionFlags flags) const { berry::IViewPart::Pointer datamanagerView = this->GetSite()->GetWorkbenchWindow()->GetActivePage()->FindView("org.mitk.views.datamanager"); if (datamanagerView.IsNull()) return; datamanagerView->GetSite()->GetSelectionProvider().Cast()->SetSelection(selection, flags); } void QmitkAbstractView::SynchronizeDataManagerSelection() const { berry::ISelection::ConstPointer currentSelection = this->GetSite()->GetSelectionProvider()->GetSelection(); if (currentSelection.IsNull()) return; SetDataManagerSelection(currentSelection); } void QmitkAbstractView::OnSelectionChanged(berry::IWorkbenchPart::Pointer /*part*/, const QList& /*nodes*/) { } void QmitkAbstractView::OnNullSelection(berry::IWorkbenchPart::Pointer /*part*/) { } QList QmitkAbstractViewPrivate::DataNodeSelectionToQList(mitk::DataNodeSelection::ConstPointer currentSelection) const { if (currentSelection.IsNull()) return QList(); return QList::fromStdList(currentSelection->GetSelectedDataNodes()); } void QmitkAbstractView::NodeAdded( const mitk::DataNode* /*node*/ ) { } void QmitkAbstractView::NodeRemoved( const mitk::DataNode* /*node*/ ) { } void QmitkAbstractView::NodeChanged( const mitk::DataNode* /*node*/ ) { } void QmitkAbstractView::FireNodeSelected( mitk::DataNode::Pointer node ) { QList nodes; nodes << node; this->FireNodesSelected(nodes); } void QmitkAbstractView::FireNodesSelected( const QList& nodes ) { // if this is the first call to FireNodesSelected and the selection provider has no QItemSelectiomMode // yet, set our helper model if (d->m_SelectionProvider->GetItemSelectionModel() == nullptr) { d->m_SelectionProvider->SetItemSelectionModel(d->m_DataNodeSelectionModel); } else if (d->m_SelectionProvider->GetItemSelectionModel() != d->m_DataNodeSelectionModel) { MITK_WARN << "A custom data node selection model has been set. Ignoring call to FireNodesSelected()."; return; } if (nodes.empty()) { d->m_DataNodeSelectionModel->clearSelection(); d->m_DataNodeItemModel->clear(); } else { // The helper data node model is just used for sending selection events. // We add the to be selected nodes and set the selection range to everything. d->m_DataNodeItemModel->clear(); foreach(mitk::DataNode::Pointer node, nodes) { d->m_DataNodeItemModel->AddDataNode(node); } d->m_DataNodeSelectionModel->select(QItemSelection(d->m_DataNodeItemModel->index(0,0), d->m_DataNodeItemModel->index(nodes.size()-1, 0)), QItemSelectionModel::ClearAndSelect); } } diff --git a/Plugins/org.mitk.gui.qt.common/src/QmitkAbstractView.h b/Plugins/org.mitk.gui.qt.common/src/QmitkAbstractView.h index 6fb4f65f85..1566bb53f5 100644 --- a/Plugins/org.mitk.gui.qt.common/src/QmitkAbstractView.h +++ b/Plugins/org.mitk.gui.qt.common/src/QmitkAbstractView.h @@ -1,363 +1,375 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #ifndef QMITKABSTRACTVIEW_H_ #define QMITKABSTRACTVIEW_H_ //# blueberry stuff #include #include #include #include //# mitk stuff #include #include "mitkDataNodeSelection.h" #include "mitkIRenderWindowPart.h" #include #include #include #include #include namespace mitk { class DataNode; } namespace berry { struct IBerryPreferences; } class QmitkAbstractViewPrivate; class QmitkAbstractViewSelectionProvider; /** * \ingroup org_mitk_gui_qt_common * * \brief A convenient base class for MITK related BlueBerry Views. * * QmitkAbstractView provides several convenience methods that ease the introduction of a new view: * *
    *
  1. Access to the DataStorage (~ the shared data repository) *
  2. Access to the active IRenderWindowPart *
  3. Access to and update notification for the view's preferences *
  4. Access to and update notification for the current DataNode selection / to DataNode selection events send through the SelectionService *
  5. Access to and update notification for DataNode events (added/removed/modified) *
  6. Methods to send DataNode selections through the SelectionService *
  7. Some minor important convenience methods (like changing the mouse cursor/exception handling) *
* * Usually all MITK Views inherit from QmitkAbstractView to achieve a consistent Workbench behavior. * * When inheriting from QmitkAbstractView, you must implement the following methods: *
    *
  • void CreateQtPartControl(QWidget* parent) *
  • void SetFocus() *
* * You may reimplement the following private virtual methods to customize your View's behavior: *
    *
  • void SetSelectionProvider() *
  • QItemSelectionModel* GetDataNodeSelectionModel() const *
* * You may reimplement the following private virtual methods to be notified about certain changes: *
    *
  • void OnSelectionChanged(berry::IWorkbenchPart::Pointer part, const QList &nodes) *
  • void OnNullSelection(berry::IWorkbenchPart::Pointer part) *
  • void OnPreferencesChanged(const berry::IBerryPreferences*) *
  • void NodeAdded(const mitk::DataNode* node) *
  • void NodeChanged(const mitk::DataNode* node) *
  • void NodeRemoved(const mitk::DataNode* node) *
  • void DataStorageModified() *
  • void DataStorageChanged(mitk::IDataStorageReference::Pointer dsRef) *
* * \see mitk::ILifecycleAwarePart * \see mitk::IZombieViewPart * \see mitk::IRenderWindowPartListener */ class MITK_QT_COMMON QmitkAbstractView : public berry::QtViewPart { public: /** * Creates smartpointer typedefs */ berryObjectMacro(QmitkAbstractView); /** * Nothing to do in the standard ctor. Initiliaze your GUI in CreateQtPartControl(QWidget*) * \see berry::QtViewPart::CreateQtPartControl(QWidget*) */ QmitkAbstractView(); /** * Disconnects all standard event listeners */ ~QmitkAbstractView() override; protected: /** * Informs other parts of the workbench that node is selected via the blueberry selection service. * * \note This method should not be used if you have set your own selection provider via * SetSelectionProvider() or your own QItemSelectionModel via GetDataNodeSelectionModel(). */ void FireNodeSelected(mitk::DataNode::Pointer node); /** * Informs other parts of the workbench that the nodes are selected via the blueberry selection service. * * \note This method should not be used if you have set your own selection provider via * SetSelectionProvider() or your own QItemSelectionModel via GetDataNodeSelectionModel(). */ virtual void FireNodesSelected(const QList& nodes); /** * \return The selection of the currently active part of the workbench or an empty list * if there is no selection or if it is empty. * * \see IsCurrentSelectionValid */ QList GetCurrentSelection() const; /** * Queries the state of the current selection. * * \return If the current selection is nullptr, this method returns * false and true otherwise. */ bool IsCurrentSelectionValid() const; /** * Returns the current selection made in the datamanager bundle or an empty list * if there is no selection or if it is empty. * * \see IsDataManagerSelectionValid */ QList GetDataManagerSelection() const; /** * Queries the state of the current selection of the data manager view. * * \return If the current data manager selection is nullptr, this method returns * false and true otherwise. */ bool IsDataManagerSelectionValid() const; /** * Sets the selection of the data manager view if available. * * \param selection The new selection for the data manager. * \param flags The Qt selection flags for controlling the way how the selection is updated. */ void SetDataManagerSelection(const berry::ISelection::ConstPointer& selection, QItemSelectionModel::SelectionFlags flags = QItemSelectionModel::ClearAndSelect) const; /** * Takes the current selection and sets it on the data manager. Only matching nodes in the * data manager view will be selected. */ void SynchronizeDataManagerSelection() const; /** * Returns the Preferences object for this View. * Important: When refering to this preferences, e.g. in a PreferencePage: The ID * for this preferences object is "/", e.g. "/org.mitk.views.datamanager" */ berry::IPreferences::Pointer GetPreferences() const; /** * Returns a reference to the currently active DataStorage. */ mitk::IDataStorageReference::Pointer GetDataStorageReference() const; /** * Returns the currently active DataStorage. */ mitk::DataStorage::Pointer GetDataStorage() const; /** * Returns the currently active mitk::IRenderWindowPart. * * \param strategies Strategies for returning a mitk::IRenderWindowPart instance if there * is currently no active one. * \return The active mitk::IRenderWindowPart. */ mitk::IRenderWindowPart* GetRenderWindowPart(mitk::WorkbenchUtil::IRenderWindowPartStrategies strategies = mitk::WorkbenchUtil::NONE) const; /** * Request an update of all render windows of the currently active IRenderWindowPart. * - * \param requestType Specifies the type of render windows for which an update - * will be requested. + * \param requestType Specifies the type of render windows for which an update will be requested. */ void RequestRenderWindowUpdate(mitk::RenderingManager::RequestType requestType = mitk::RenderingManager::REQUEST_UPDATE_ALL); + /** + * Initialize the specified render windows to the given reference geometry. + * + * \param referenceGeometry The reference geometry which is used for updating the current world geometry + * \param requestType Specifies the type of render windows for which an update will be requested + * \param resetCamera If true, the camera and crosshair will be reset to the default view (centered, no zoom). + * If false, the current crosshair position will be stored and reset after the views have been + * initialized. The camera zoom will be kept after the view initialization. + */ + void InitializeRenderWindows(const mitk::TimeGeometry *referenceGeometry, + mitk::RenderingManager::RequestType requestType, + bool resetCamera); + /** * Outputs an error message to the console and displays a message box containing * the exception description. * \param e the exception which should be handled * \param parent * \param showDialog controls, whether additionally a message box should be * displayed to inform the user that something went wrong */ void HandleException( std::exception& e, QWidget* parent = nullptr, bool showDialog = true ) const; /** * Calls HandleException ( std::exception&, QWidget*, bool ) internally * \see HandleException ( std::exception&, QWidget*, bool ) */ void HandleException( const char* str, QWidget* parent = nullptr, bool showDialog = true ) const; /** * Convenient method to set and reset a wait cursor ("hourglass") */ void WaitCursorOn(); /** * Convenient method to restore the standard cursor */ void WaitCursorOff(); /** * Convenient method to set and reset a busy cursor */ void BusyCursorOn(); /** * Convenient method to restore the standard cursor */ void BusyCursorOff(); /** * Convenient method to restore the standard cursor */ void RestoreOverrideCursor(); private: /** * Reimplement this method to set a custom selection provider. This method is * called once after CreateQtPartControl(). * * The default implementation registers a QmitkDataNodeSelectionProvider with * a QItemSelectionModel returned by GetDataNodeSelectionModel(). */ virtual void SetSelectionProvider(); /** * Reimplement this method to supply a custom Qt selection model. The custom * model will be used with the default selection provider QmitkDataNodeSelectionProvider * to inform the MITK Workbench about selection changes. * * If you reimplement this method, the methods FireNodeSelected() and FireNodesSelected() * will have no effect. Use your custom selection model to notify the MITK Workbench * about selection changes. * * The Qt item model used with the custom selection model must return mitk::DataNode::Pointer * objects for model indexes when the role is QmitkDataNodeRole. */ virtual QItemSelectionModel* GetDataNodeSelectionModel() const; /** * Called when the selection in the workbench changed. * May be reimplemented by deriving classes. * * \param part The source part responsible for the selection change. * \param nodes A list of selected nodes. * * \see OnNullSelection */ virtual void OnSelectionChanged(berry::IWorkbenchPart::Pointer part, const QList &nodes); /** * Called when a nullptr selection occurs. * * \param part The source part responsible for the selection change. */ virtual void OnNullSelection(berry::IWorkbenchPart::Pointer part); /** * Called when the preferences object of this view changed. * May be reimplemented by deriving classes. * * \see GetPreferences() */ virtual void OnPreferencesChanged(const berry::IBerryPreferences*); /** * Called when a DataStorage Add event was thrown. May be reimplemented * by deriving classes. */ virtual void NodeAdded(const mitk::DataNode* node); /** * Called when a DataStorage Changed event was thrown. May be reimplemented * by deriving classes. */ virtual void NodeChanged(const mitk::DataNode* node); /** * Called when a DataStorage Remove event was thrown. May be reimplemented * by deriving classes. */ virtual void NodeRemoved(const mitk::DataNode* node); /** * Called when a DataStorage add *or* remove *or* change event from the currently active * data storage is thrown. * * May be reimplemented by deriving classes. */ virtual void DataStorageModified(); /** * Called when the currently active DataStorage changed. * May be reimplemented by deriving classes. * * \param dsRef A reference to the new active DataStorage. */ virtual void DataStorageChanged(mitk::IDataStorageReference::Pointer dsRef); /** * Creates a scroll area for this view and calls CreateQtPartControl then */ void CreatePartControl(QWidget* parent) override; /** * Called immediately after CreateQtPartControl(). * Here standard event listeners for a QmitkAbstractView are registered */ void AfterCreateQtPartControl(); private: friend class QmitkAbstractViewPrivate; friend class QmitkViewCoordinator; Q_DISABLE_COPY(QmitkAbstractView) const QScopedPointer d; }; #endif /*QMITKABSTRACTVIEW_H_*/ diff --git a/Plugins/org.mitk.gui.qt.segmentation/src/internal/QmitkSegmentationView.cpp b/Plugins/org.mitk.gui.qt.segmentation/src/internal/QmitkSegmentationView.cpp index 1c752436ff..e35c714c0d 100644 --- a/Plugins/org.mitk.gui.qt.segmentation/src/internal/QmitkSegmentationView.cpp +++ b/Plugins/org.mitk.gui.qt.segmentation/src/internal/QmitkSegmentationView.cpp @@ -1,923 +1,909 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #include #include "mitkProperties.h" #include "mitkSegTool2D.h" #include "mitkStatusBar.h" #include "QmitkNewSegmentationDialog.h" #include #include #include #include "QmitkSegmentationView.h" #include #include "mitkVtkResliceInterpolationProperty.h" #include "mitkApplicationCursor.h" #include "mitkSegmentationObjectFactory.h" #include "mitkPluginActivator.h" #include "mitkCameraController.h" #include "mitkLabelSetImage.h" #include "mitkImageTimeSelector.h" #include "mitkNodePredicateSubGeometry.h" #include #include "usModuleResource.h" #include "usModuleResourceStream.h" //micro service to get the ToolManager instance #include "mitkToolManagerProvider.h" #include #include const std::string QmitkSegmentationView::VIEW_ID = "org.mitk.views.segmentation"; QmitkSegmentationView::QmitkSegmentationView() : m_Parent(nullptr) , m_Controls(nullptr) , m_RenderWindowPart(nullptr) , m_MouseCursorSet(false) , m_DataSelectionChanged(false) { mitk::TNodePredicateDataType::Pointer isImage = mitk::TNodePredicateDataType::New(); mitk::NodePredicateDataType::Pointer isDwi = mitk::NodePredicateDataType::New("DiffusionImage"); mitk::NodePredicateDataType::Pointer isDti = mitk::NodePredicateDataType::New("TensorImage"); mitk::NodePredicateDataType::Pointer isOdf = mitk::NodePredicateDataType::New("OdfImage"); auto isSegment = mitk::NodePredicateDataType::New("Segment"); mitk::NodePredicateOr::Pointer validImages = mitk::NodePredicateOr::New(); validImages->AddPredicate(mitk::NodePredicateAnd::New(isImage, mitk::NodePredicateNot::New(isSegment))); validImages->AddPredicate(isDwi); validImages->AddPredicate(isDti); validImages->AddPredicate(isOdf); m_IsNotAHelperObject = mitk::NodePredicateNot::New(mitk::NodePredicateProperty::New("helper object", mitk::BoolProperty::New(true))); m_IsOfTypeImagePredicate = mitk::NodePredicateAnd::New(validImages, m_IsNotAHelperObject); mitk::NodePredicateProperty::Pointer isBinaryPredicate = mitk::NodePredicateProperty::New("binary", mitk::BoolProperty::New(true)); mitk::NodePredicateNot::Pointer isNotBinaryPredicate = mitk::NodePredicateNot::New(isBinaryPredicate); mitk::NodePredicateAnd::Pointer isABinaryImagePredicate = mitk::NodePredicateAnd::New(m_IsOfTypeImagePredicate, isBinaryPredicate); mitk::NodePredicateAnd::Pointer isNotABinaryImagePredicate = mitk::NodePredicateAnd::New(m_IsOfTypeImagePredicate, isNotBinaryPredicate); auto isMLImageType = mitk::TNodePredicateDataType::New(); mitk::NodePredicateAnd::Pointer isAMLImagePredicate = mitk::NodePredicateAnd::New(isMLImageType, m_IsNotAHelperObject); mitk::NodePredicateAnd::Pointer isNotAMLImagePredicate = mitk::NodePredicateAnd::New(mitk::NodePredicateNot::New(isMLImageType), m_IsNotAHelperObject); m_IsASegmentationImagePredicate = mitk::NodePredicateOr::New(isABinaryImagePredicate, isAMLImagePredicate); m_IsAPatientImagePredicate = mitk::NodePredicateAnd::New(isNotABinaryImagePredicate, isNotAMLImagePredicate); } QmitkSegmentationView::~QmitkSegmentationView() { if (m_Controls) { SetToolSelectionBoxesEnabled(false); // deactivate all tools mitk::ToolManagerProvider::GetInstance()->GetToolManager()->ActivateTool(-1); // removing all observers for (NodeTagMapType::iterator dataIter = m_WorkingDataObserverTags.begin(); dataIter != m_WorkingDataObserverTags.end(); ++dataIter) { (*dataIter).first->GetProperty("visible")->RemoveObserver((*dataIter).second); } m_WorkingDataObserverTags.clear(); mitk::RenderingManager::GetInstance()->RemoveObserver(m_RenderingManagerObserverTag); ctkPluginContext* context = mitk::PluginActivator::getContext(); ctkServiceReference ppmRef = context->getServiceReference(); mitk::PlanePositionManagerService* service = context->getService(ppmRef); service->RemoveAllPlanePositions(); context->ungetService(ppmRef); SetToolManagerSelection(nullptr, nullptr); } delete m_Controls; } void QmitkSegmentationView::NewNodeObjectsGenerated(mitk::ToolManager::DataVectorType* nodes) { if (!nodes) return; mitk::ToolManager* toolManager = mitk::ToolManagerProvider::GetInstance()->GetToolManager(); if (!toolManager) return; for (mitk::ToolManager::DataVectorType::iterator iter = nodes->begin(); iter != nodes->end(); ++iter) { this->FireNodeSelected( *iter ); // only last iteration meaningful, multiple generated objects are not taken into account here } } void QmitkSegmentationView::RenderWindowPartActivated(mitk::IRenderWindowPart* renderWindowPart) { if (m_RenderWindowPart != renderWindowPart) { m_RenderWindowPart = renderWindowPart; } if (m_Parent) { m_Parent->setEnabled(true); } // tell the interpolation about tool manager, data storage and render window part if (m_Controls) { mitk::ToolManager* toolManager = mitk::ToolManagerProvider::GetInstance()->GetToolManager(); m_Controls->m_SlicesInterpolator->SetDataStorage(this->GetDataStorage()); QList controllers; controllers.push_back(renderWindowPart->GetQmitkRenderWindow("axial")->GetSliceNavigationController()); controllers.push_back(renderWindowPart->GetQmitkRenderWindow("sagittal")->GetSliceNavigationController()); controllers.push_back(renderWindowPart->GetQmitkRenderWindow("coronal")->GetSliceNavigationController()); m_Controls->m_SlicesInterpolator->Initialize(toolManager, controllers); } } void QmitkSegmentationView::RenderWindowPartDeactivated(mitk::IRenderWindowPart* /*renderWindowPart*/) { m_RenderWindowPart = nullptr; if (m_Parent) { m_Parent->setEnabled(false); } } void QmitkSegmentationView::OnPreferencesChanged(const berry::IBerryPreferences* prefs) { if (m_Controls != nullptr) { bool slimView = prefs->GetBool("slim view", false); m_Controls->m_ManualToolSelectionBox2D->SetShowNames(!slimView); m_Controls->m_ManualToolSelectionBox3D->SetShowNames(!slimView); m_Controls->btnNewSegmentation->setToolButtonStyle(slimView ? Qt::ToolButtonIconOnly : Qt::ToolButtonTextOnly); } auto autoSelectionEnabled = prefs->GetBool("auto selection", true); m_Controls->patImageSelector->SetAutoSelectNewNodes(autoSelectionEnabled); m_Controls->segImageSelector->SetAutoSelectNewNodes(autoSelectionEnabled); this->ForceDisplayPreferencesUponAllImages(); } void QmitkSegmentationView::CreateNewSegmentation() { mitk::DataNode::Pointer node = mitk::ToolManagerProvider::GetInstance()->GetToolManager()->GetReferenceData(0); if (node.IsNull()) { MITK_ERROR << "'Create new segmentation' button should never be clickable unless a reference image is selected."; return; } mitk::Image::ConstPointer referenceImage = dynamic_cast(node->GetData()); if (referenceImage.IsNull()) { MITK_ERROR << "Reference data needs to be an image in order to create a new segmentation."; return; } if (referenceImage->GetDimension() <= 1) { QMessageBox::information(nullptr, tr("Segmentation"), tr("Segmentation is currently not supported for 2D images")); return; } // ask about the name and organ type of the new segmentation auto dialog = new QmitkNewSegmentationDialog(m_Parent); // needs a QWidget as parent, "this" is not QWidget QStringList organColors = mitk::OrganNamesHandling::GetDefaultOrganColorString(); dialog->SetSuggestionList(organColors); int dialogReturnValue = dialog->exec(); if (dialogReturnValue == QDialog::Rejected) { // user clicked Cancel or pressed Esc or something similar return; } const auto currentTimePoint = mitk::RenderingManager::GetInstance()->GetTimeNavigationController()->GetSelectedTimePoint(); unsigned int imageTimeStep = 0; if (referenceImage->GetTimeGeometry()->IsValidTimePoint(currentTimePoint)) { imageTimeStep = referenceImage->GetTimeGeometry()->TimePointToTimeStep(currentTimePoint); } auto segTemplateImage = referenceImage; if (referenceImage->GetDimension() > 3) { auto result = QMessageBox::question(m_Parent, tr("Create a static or dynamic segmentation?"), tr("The patient image has multiple time steps.\n\nDo you want to create a static " "segmentation that is identical for all time steps or do you want to create a " "dynamic segmentation to segment individual time steps?"), tr("Create static segmentation"), tr("Create dynamic segmentation"), QString(), 0, 0); if (result == 0) { auto selector = mitk::ImageTimeSelector::New(); selector->SetInput(referenceImage); selector->SetTimeNr(0); selector->Update(); const auto refTimeGeometry = referenceImage->GetTimeGeometry(); auto newTimeGeometry = mitk::ProportionalTimeGeometry::New(); newTimeGeometry->SetFirstTimePoint(refTimeGeometry->GetMinimumTimePoint()); newTimeGeometry->SetStepDuration(refTimeGeometry->GetMaximumTimePoint() - refTimeGeometry->GetMinimumTimePoint()); mitk::Image::Pointer newImage = selector->GetOutput(); newTimeGeometry->SetTimeStepGeometry(referenceImage->GetGeometry(imageTimeStep), 0); newImage->SetTimeGeometry(newTimeGeometry); segTemplateImage = newImage; } } // create a new image of the same dimensions and smallest possible pixel type auto toolManager = mitk::ToolManagerProvider::GetInstance()->GetToolManager(); auto firstTool = toolManager->GetToolById(0); if (nullptr == firstTool) { return; } std::string newNodeName = dialog->GetSegmentationName().toStdString(); if (newNodeName.empty()) { newNodeName = "no_name"; } mitk::DataNode::Pointer emptySegmentation = nullptr; try { emptySegmentation = firstTool->CreateEmptySegmentationNode(segTemplateImage, newNodeName, dialog->GetColor()); } catch (const std::bad_alloc &) { QMessageBox::warning(nullptr, tr("Create new segmentation"), tr("Could not allocate memory for new segmentation")); } if (nullptr == emptySegmentation) { return; // could have been aborted by user } // initialize "showVolume"-property to false to prevent recalculating the volume while working on the segmentation emptySegmentation->SetProperty("showVolume", mitk::BoolProperty::New(false)); mitk::OrganNamesHandling::UpdateOrganList(organColors, dialog->GetSegmentationName(), dialog->GetColor()); // escape ';' here (replace by '\;') QString stringForStorage = organColors.replaceInStrings(";", "\\;").join(";"); MITK_DEBUG << "Will store: " << stringForStorage; this->GetPreferences()->Put("Organ-Color-List", stringForStorage); this->GetPreferences()->Flush(); this->GetDataStorage()->Add(emptySegmentation, node); if (mitk::ToolManagerProvider::GetInstance()->GetToolManager()->GetWorkingData(0)) { mitk::ToolManagerProvider::GetInstance()->GetToolManager()->GetWorkingData(0)->SetSelected(false); } emptySegmentation->SetSelected(true); m_Controls->segImageSelector->SetCurrentSelectedNode(emptySegmentation); - mitk::Point3D currentPosition = mitk::Point3D(); - if (nullptr != m_RenderWindowPart) - { - currentPosition = m_RenderWindowPart->GetSelectedPosition(); - } - - mitk::RenderingManager::GetInstance()->InitializeViews( - referenceImage->GetTimeGeometry(), mitk::RenderingManager::REQUEST_UPDATE_ALL, false); - - if (nullptr != m_RenderWindowPart) - { - m_RenderWindowPart->SetSelectedPosition(currentPosition); - } - - mitk::RenderingManager::GetInstance()->GetTimeNavigationController()->GetTime()->SetPos(imageTimeStep); + this->InitializeRenderWindows(referenceImage->GetTimeGeometry(), mitk::RenderingManager::REQUEST_UPDATE_ALL, false); } void QmitkSegmentationView::OnVisiblePropertyChanged() { this->CheckRenderingState(); } void QmitkSegmentationView::NodeAdded(const mitk::DataNode *node) { if (!m_IsASegmentationImagePredicate->CheckNode(node)) { return; } itk::SimpleMemberCommand::Pointer command = itk::SimpleMemberCommand::New(); command->SetCallbackFunction(this, &QmitkSegmentationView::OnVisiblePropertyChanged); m_WorkingDataObserverTags.insert(std::pair(const_cast(node), node->GetProperty("visible")->AddObserver(itk::ModifiedEvent(), command))); ApplyDisplayOptions(const_cast(node)); } void QmitkSegmentationView::NodeRemoved(const mitk::DataNode* node) { if (m_IsASegmentationImagePredicate->CheckNode(node)) { //First of all remove all possible contour markers of the segmentation mitk::DataStorage::SetOfObjects::ConstPointer allContourMarkers = this->GetDataStorage()->GetDerivations(node, mitk::NodePredicateProperty::New("isContourMarker", mitk::BoolProperty::New(true))); ctkPluginContext* context = mitk::PluginActivator::getContext(); ctkServiceReference ppmRef = context->getServiceReference(); mitk::PlanePositionManagerService* service = context->getService(ppmRef); for (mitk::DataStorage::SetOfObjects::ConstIterator it = allContourMarkers->Begin(); it != allContourMarkers->End(); ++it) { std::string nodeName = node->GetName(); unsigned int t = nodeName.find_last_of(" "); unsigned int id = atof(nodeName.substr(t + 1).c_str()) - 1; service->RemovePlanePosition(id); this->GetDataStorage()->Remove(it->Value()); } context->ungetService(ppmRef); service = nullptr; if ((mitk::ToolManagerProvider::GetInstance()->GetToolManager()->GetWorkingData(0) == node) && m_Controls->patImageSelector->GetSelectedNode().IsNotNull()) { this->SetToolManagerSelection(mitk::ToolManagerProvider::GetInstance()->GetToolManager()->GetReferenceData(0), nullptr); this->UpdateWarningLabel(tr("Select or create a segmentation")); } mitk::Image* image = dynamic_cast(node->GetData()); mitk::SurfaceInterpolationController::GetInstance()->RemoveInterpolationSession(image); } mitk::DataNode* tempNode = const_cast(node); //Remove observer if one was registered auto finding = m_WorkingDataObserverTags.find(tempNode); if (finding != m_WorkingDataObserverTags.end()) { node->GetProperty("visible")->RemoveObserver(m_WorkingDataObserverTags[tempNode]); m_WorkingDataObserverTags.erase(tempNode); } } void QmitkSegmentationView::OnPatientSelectionChanged(QList nodes) { if(! nodes.empty()) { this->UpdateWarningLabel(""); auto node = nodes.first(); auto segPredicate = mitk::NodePredicateAnd::New(m_IsASegmentationImagePredicate.GetPointer(), mitk::NodePredicateSubGeometry::New(node->GetData()->GetGeometry())); m_Controls->segImageSelector->SetNodePredicate(segPredicate); mitk::DataNode* segNode = m_Controls->segImageSelector->GetSelectedNode(); this->SetToolManagerSelection(node, segNode); if (segNode) { //Doing this we can assure that the segmentation is always visible if the segmentation and the patient image are //loaded separately int layer(10); node->GetIntProperty("layer", layer); layer++; segNode->SetProperty("layer", mitk::IntProperty::New(layer)); this->CheckRenderingState(); } else { this->SetToolSelectionBoxesEnabled( false ); this->UpdateWarningLabel(tr("Select or create a segmentation")); } } else { m_Controls->segImageSelector->SetNodePredicate(m_IsASegmentationImagePredicate); this->UpdateWarningLabel(tr("Please select an image!")); this->SetToolSelectionBoxesEnabled( false ); } } void QmitkSegmentationView::OnSegmentationSelectionChanged(QList nodes) { if (nodes.empty()) { this->UpdateWarningLabel(tr("Select or create a segmentation")); this->SetToolSelectionBoxesEnabled( false ); return; } auto refNode = m_Controls->patImageSelector->GetSelectedNode(); auto segNode = nodes.front(); if (!refNode) { this->UpdateWarningLabel(tr("Please select the matching patient image!")); this->SetToolSelectionBoxesEnabled(false); this->SetToolManagerSelection(nullptr, segNode); return; } this->CheckRenderingState(); if ( m_Controls->lblSegmentationWarnings->isVisible()) // "this->CheckRenderingState()" caused a warning. we do not need to go any further return; this->SetToolManagerSelection(refNode, segNode); if (segNode) { //Doing this we can assure that the segmenation is always visible if the segmentation and the patient image are //loaded separately int layer(10); refNode->GetIntProperty("layer", layer); layer++; segNode->SetProperty("layer", mitk::IntProperty::New(layer)); } else { this->SetToolSelectionBoxesEnabled(false); this->UpdateWarningLabel(tr("Select or create a segmentation")); } mitk::IRenderWindowPart* renderWindowPart = this->GetRenderWindowPart(); if (!renderWindowPart || !segNode->IsVisible(renderWindowPart->GetQmitkRenderWindow("axial")->GetRenderer())) { this->UpdateWarningLabel(tr("The selected segmentation is currently not visible!")); this->SetToolSelectionBoxesEnabled( false ); } } void QmitkSegmentationView::OnShowMarkerNodes (bool state) { mitk::SegTool2D::Pointer manualSegmentationTool; unsigned int numberOfExistingTools = mitk::ToolManagerProvider::GetInstance()->GetToolManager()->GetTools().size(); for(unsigned int i = 0; i < numberOfExistingTools; i++) { manualSegmentationTool = dynamic_cast(mitk::ToolManagerProvider::GetInstance()->GetToolManager()->GetToolById(i)); if (manualSegmentationTool) { if(state == true) { manualSegmentationTool->SetShowMarkerNodes( true ); } else { manualSegmentationTool->SetShowMarkerNodes( false ); } } } } void QmitkSegmentationView::OnContourMarkerSelected(const mitk::DataNode *node) { QmitkRenderWindow* selectedRenderWindow = nullptr; auto* renderWindowPart = this->GetRenderWindowPart(mitk::WorkbenchUtil::OPEN); auto* axialRenderWindow = renderWindowPart->GetQmitkRenderWindow("axial"); auto* sagittalRenderWindow = renderWindowPart->GetQmitkRenderWindow("sagittal"); auto* coronalRenderWindow = renderWindowPart->GetQmitkRenderWindow("coronal"); auto* _3DRenderWindow = renderWindowPart->GetQmitkRenderWindow("3d"); bool PlanarFigureInitializedWindow = false; // find initialized renderwindow if (node->GetBoolProperty("PlanarFigureInitializedWindow", PlanarFigureInitializedWindow, axialRenderWindow->GetRenderer())) { selectedRenderWindow = axialRenderWindow; } if (!selectedRenderWindow && node->GetBoolProperty( "PlanarFigureInitializedWindow", PlanarFigureInitializedWindow, sagittalRenderWindow->GetRenderer())) { selectedRenderWindow = sagittalRenderWindow; } if (!selectedRenderWindow && node->GetBoolProperty( "PlanarFigureInitializedWindow", PlanarFigureInitializedWindow, coronalRenderWindow->GetRenderer())) { selectedRenderWindow = coronalRenderWindow; } if (!selectedRenderWindow && node->GetBoolProperty( "PlanarFigureInitializedWindow", PlanarFigureInitializedWindow, _3DRenderWindow->GetRenderer())) { selectedRenderWindow = _3DRenderWindow; } // make node visible if (selectedRenderWindow) { std::string nodeName = node->GetName(); unsigned int t = nodeName.find_last_of(" "); unsigned int id = atof(nodeName.substr(t+1).c_str())-1; { ctkPluginContext* context = mitk::PluginActivator::getContext(); ctkServiceReference ppmRef = context->getServiceReference(); mitk::PlanePositionManagerService* service = context->getService(ppmRef); selectedRenderWindow->GetSliceNavigationController()->ExecuteOperation(service->GetPlanePosition(id)); context->ungetService(ppmRef); } selectedRenderWindow->GetRenderer()->GetCameraController()->Fit(); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } } void QmitkSegmentationView::OnSelectionChanged(berry::IWorkbenchPart::Pointer /*part*/, const QList &nodes) { if (nodes.size() != 0) { std::string markerName = "Position"; unsigned int numberOfNodes = nodes.size(); std::string nodeName = nodes.at(0)->GetName(); if ((numberOfNodes == 1) && (nodeName.find(markerName) == 0)) { this->OnContourMarkerSelected(nodes.at(0)); return; } } } void QmitkSegmentationView::OnTabWidgetChanged(int id) { //always disable tools on tab changed mitk::ToolManagerProvider::GetInstance()->GetToolManager()->ActivateTool(-1); //2D Tab ID = 0 //3D Tab ID = 1 if (id == 0) { //Hide 3D selection box, show 2D selection box m_Controls->m_ManualToolSelectionBox3D->hide(); m_Controls->m_ManualToolSelectionBox2D->show(); //Deactivate possible active tool //TODO Remove possible visible interpolations -> Maybe changes in SlicesInterpolator } else { //Hide 3D selection box, show 2D selection box m_Controls->m_ManualToolSelectionBox2D->hide(); m_Controls->m_ManualToolSelectionBox3D->show(); //Deactivate possible active tool } } void QmitkSegmentationView::SetToolManagerSelection(mitk::DataNode* referenceData, mitk::DataNode* workingData) { // called as a result of new BlueBerry selections // tells the ToolManager for manual segmentation about new selections // updates GUI information about what the user should select mitk::ToolManager* toolManager = mitk::ToolManagerProvider::GetInstance()->GetToolManager(); toolManager->SetReferenceData(const_cast(referenceData)); toolManager->SetWorkingData(const_cast(workingData)); m_Controls->btnNewSegmentation->setEnabled(referenceData != nullptr); } void QmitkSegmentationView::ForceDisplayPreferencesUponAllImages() { if (!m_Parent) { return; } // check all images and segmentations in DataStorage: // (items in brackets are implicitly done by previous steps) // 1. // if a reference image is selected, // show the reference image // and hide all other images (orignal and segmentation), // (and hide all segmentations of the other original images) // and show all the reference's segmentations // if no reference image is selected, do do nothing // // 2. // if a segmentation is selected, // show it // (and hide all all its siblings (childs of the same parent, incl, nullptr parent)) // if no segmentation is selected, do nothing if (!m_Controls) { return; // might happen on initialization (preferences loaded) } mitk::ToolManager* toolManager = mitk::ToolManagerProvider::GetInstance()->GetToolManager(); mitk::DataNode::Pointer referenceData = toolManager->GetReferenceData(0); mitk::DataNode::Pointer workingData = toolManager->GetWorkingData(0); // 1. if (referenceData.IsNotNull()) { // iterate all images mitk::DataStorage::SetOfObjects::ConstPointer allImages = this->GetDataStorage()->GetSubset(m_IsASegmentationImagePredicate); for ( mitk::DataStorage::SetOfObjects::const_iterator iter = allImages->begin(); iter != allImages->end(); ++iter) { mitk::DataNode* node = *iter; // apply display preferences ApplyDisplayOptions(node); // set visibility node->SetVisibility(node == referenceData); } } // 2. if (workingData.IsNotNull()) workingData->SetVisibility(true); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } void QmitkSegmentationView::ApplyDisplayOptions(mitk::DataNode* node) { if (!node) { return; } mitk::BoolProperty::Pointer drawOutline = mitk::BoolProperty::New(GetPreferences()->GetBool("draw outline", true)); mitk::LabelSetImage* labelSetImage = dynamic_cast(node->GetData()); if (nullptr != labelSetImage) { // node is actually a multi label segmentation, // but its outline property can be set in the 'single label' segmentation preference page as well node->SetProperty("labelset.contour.active", drawOutline); //node->SetProperty("opacity", mitk::FloatProperty::New(drawOutline->GetValue() ? 1.0f : 0.3f)); // force render window update to show outline node->GetData()->Modified(); } else { // node is a 'single label' segmentation bool isBinary = false; node->GetBoolProperty("binary", isBinary); if (isBinary) { node->SetProperty("outline binary", drawOutline); node->SetProperty("outline width", mitk::FloatProperty::New(2.0)); //node->SetProperty("opacity", mitk::FloatProperty::New(drawOutline->GetValue() ? 1.0f : 0.3f)); // force render window update to show outline node->GetData()->Modified(); } } } void QmitkSegmentationView::CheckRenderingState() { mitk::IRenderWindowPart* renderWindowPart = this->GetRenderWindowPart(); mitk::DataNode* workingNode = m_Controls->segImageSelector->GetSelectedNode(); if (!workingNode) { this->SetToolSelectionBoxesEnabled(false); this->UpdateWarningLabel(tr("Select or create a segmentation")); return; } bool selectedNodeIsVisible = renderWindowPart && workingNode->IsVisible(renderWindowPart->GetQmitkRenderWindow("axial")->GetRenderer()); if (!selectedNodeIsVisible) { this->SetToolSelectionBoxesEnabled(false); this->UpdateWarningLabel(tr("The selected segmentation is currently not visible!")); return; } /* * Here we check whether the geometry of the selected segmentation image if aligned with the worldgeometry * At the moment it is not supported to use a geometry different from the selected image for reslicing. * For further information see Bug 16063 */ const mitk::BaseGeometry *workingNodeGeo = workingNode->GetData()->GetGeometry(); const mitk::BaseGeometry* worldGeo = renderWindowPart->GetQmitkRenderWindow("3d")->GetSliceNavigationController()->GetCurrentGeometry3D(); if (nullptr != workingNodeGeo && nullptr != worldGeo) { if (mitk::Equal(*workingNodeGeo->GetBoundingBox(), *worldGeo->GetBoundingBox(), mitk::eps, true)) { this->SetToolManagerSelection(m_Controls->patImageSelector->GetSelectedNode(), workingNode); this->SetToolSelectionBoxesEnabled(true); this->UpdateWarningLabel(""); return; } } this->SetToolManagerSelection(m_Controls->patImageSelector->GetSelectedNode(), nullptr); this->SetToolSelectionBoxesEnabled(false); this->UpdateWarningLabel(tr("Please perform a reinit on the segmentation image!")); } void QmitkSegmentationView::UpdateWarningLabel(QString text) { if (text.size() == 0) m_Controls->lblSegmentationWarnings->hide(); else m_Controls->lblSegmentationWarnings->show(); m_Controls->lblSegmentationWarnings->setText("" + text + ""); } void QmitkSegmentationView::CreateQtPartControl(QWidget* parent) { // setup the basic GUI of this view m_Parent = parent; m_Controls = new Ui::QmitkSegmentationControls; m_Controls->setupUi(parent); m_Controls->patImageSelector->SetDataStorage(GetDataStorage()); m_Controls->patImageSelector->SetNodePredicate(m_IsAPatientImagePredicate); m_Controls->patImageSelector->SetSelectionIsOptional(false); m_Controls->patImageSelector->SetInvalidInfo("Select an image."); m_Controls->patImageSelector->SetPopUpTitel("Select an image."); m_Controls->patImageSelector->SetPopUpHint("Select an image that should be used to define the geometry and bounds of the segmentation."); UpdateWarningLabel(tr("Please select an image")); if (m_Controls->patImageSelector->GetSelectedNode().IsNotNull()) { UpdateWarningLabel(tr("Select or create a new segmentation")); } m_Controls->segImageSelector->SetDataStorage(GetDataStorage()); m_Controls->segImageSelector->SetNodePredicate(m_IsASegmentationImagePredicate); m_Controls->segImageSelector->SetSelectionIsOptional(false); m_Controls->segImageSelector->SetInvalidInfo("Select a segmentation."); m_Controls->segImageSelector->SetPopUpTitel("Select a segmentation."); m_Controls->segImageSelector->SetPopUpHint("Select a segmentation that should be modified. Only segmentation with the same geometry and within the bounds of the reference image are selected."); if (m_Controls->segImageSelector->GetSelectedNode().IsNotNull()) { UpdateWarningLabel(""); } mitk::ToolManager* toolManager = mitk::ToolManagerProvider::GetInstance()->GetToolManager(); assert(toolManager); toolManager->SetDataStorage(*(GetDataStorage())); toolManager->InitializeTools(); QString segTools2D = tr("Add Subtract Paint Wipe 'Region Growing' Fill Erase 'Live Wire' '2D Fast Marching'"); QString segTools3D = tr("Threshold 'UL Threshold' Otsu 'Fast Marching 3D' 'Region Growing 3D' Watershed Picking"); std::regex extSegTool2DRegEx("SegTool2D$"); std::regex extSegTool3DRegEx("SegTool3D$"); auto tools = toolManager->GetTools(); for (const auto &tool : tools) { if (std::regex_search(tool->GetNameOfClass(), extSegTool2DRegEx)) { segTools2D.append(QString(" '%1'").arg(tool->GetName())); } else if (std::regex_search(tool->GetNameOfClass(), extSegTool3DRegEx)) { segTools3D.append(QString(" '%1'").arg(tool->GetName())); } } // all part of open source MITK m_Controls->m_ManualToolSelectionBox2D->setEnabled(true); m_Controls->m_ManualToolSelectionBox2D->SetGenerateAccelerators(true); m_Controls->m_ManualToolSelectionBox2D->SetToolGUIArea(m_Controls->m_ManualToolGUIContainer2D); m_Controls->m_ManualToolSelectionBox2D->SetDisplayedToolGroups(segTools2D.toStdString()); m_Controls->m_ManualToolSelectionBox2D->SetLayoutColumns(3); m_Controls->m_ManualToolSelectionBox2D->SetEnabledMode(QmitkToolSelectionBox::EnabledWithReferenceAndWorkingDataVisible); connect(m_Controls->m_ManualToolSelectionBox2D, &QmitkToolSelectionBox::ToolSelected, this, &QmitkSegmentationView::OnManualTool2DSelected); //setup 3D Tools m_Controls->m_ManualToolSelectionBox3D->setEnabled(true); m_Controls->m_ManualToolSelectionBox3D->SetGenerateAccelerators(true); m_Controls->m_ManualToolSelectionBox3D->SetToolGUIArea(m_Controls->m_ManualToolGUIContainer3D); //specify tools to be added to 3D Tool area m_Controls->m_ManualToolSelectionBox3D->SetDisplayedToolGroups(segTools3D.toStdString()); m_Controls->m_ManualToolSelectionBox3D->SetLayoutColumns(3); m_Controls->m_ManualToolSelectionBox3D->SetEnabledMode(QmitkToolSelectionBox::EnabledWithReferenceAndWorkingDataVisible); //Hide 3D selection box, show 2D selection box m_Controls->m_ManualToolSelectionBox3D->hide(); m_Controls->m_ManualToolSelectionBox2D->show(); // update the list of segmentations toolManager->NewNodeObjectsGenerated += mitk::MessageDelegate1(this, &QmitkSegmentationView::NewNodeObjectsGenerated); // create signal/slot connections connect(m_Controls->patImageSelector, &QmitkAbstractNodeSelectionWidget::CurrentSelectionChanged, this, &QmitkSegmentationView::OnPatientSelectionChanged); connect(m_Controls->segImageSelector, &QmitkAbstractNodeSelectionWidget::CurrentSelectionChanged, this, &QmitkSegmentationView::OnSegmentationSelectionChanged); connect(m_Controls->btnNewSegmentation, &QToolButton::clicked, this, &QmitkSegmentationView::CreateNewSegmentation); connect(m_Controls->tabWidgetSegmentationTools, &QTabWidget::currentChanged, this, &QmitkSegmentationView::OnTabWidgetChanged); connect(m_Controls->m_SlicesInterpolator, &QmitkSlicesInterpolator::SignalShowMarkerNodes, this, &QmitkSegmentationView::OnShowMarkerNodes); // set callback function for already existing nodes (images & segmentations) mitk::DataStorage::SetOfObjects::ConstPointer allImages = GetDataStorage()->GetSubset(m_IsOfTypeImagePredicate); for (mitk::DataStorage::SetOfObjects::const_iterator iter = allImages->begin(); iter != allImages->end(); ++iter) { mitk::DataNode* node = *iter; itk::SimpleMemberCommand::Pointer command = itk::SimpleMemberCommand::New(); command->SetCallbackFunction(this, &QmitkSegmentationView::OnVisiblePropertyChanged); m_WorkingDataObserverTags.insert(std::pair(node, node->GetProperty("visible")->AddObserver(itk::ModifiedEvent(), command))); } itk::SimpleMemberCommand::Pointer command = itk::SimpleMemberCommand::New(); command->SetCallbackFunction(this, &QmitkSegmentationView::CheckRenderingState); m_RenderingManagerObserverTag = mitk::RenderingManager::GetInstance()->AddObserver(mitk::RenderingManagerViewsInitializedEvent(), command); SetToolManagerSelection(m_Controls->patImageSelector->GetSelectedNode(), m_Controls->segImageSelector->GetSelectedNode()); m_RenderWindowPart = this->GetRenderWindowPart(); if (nullptr != m_RenderWindowPart) this->RenderWindowPartActivated(m_RenderWindowPart); //Should be done last, if everything else is configured because it triggers the autoselection of data. m_Controls->patImageSelector->SetAutoSelectNewNodes(true); m_Controls->segImageSelector->SetAutoSelectNewNodes(true); } void QmitkSegmentationView::SetFocus() { m_Controls->btnNewSegmentation->setFocus(); } void QmitkSegmentationView::OnManualTool2DSelected(int id) { if (id >= 0) { std::string text = "Active Tool: \""; mitk::ToolManager* toolManager = mitk::ToolManagerProvider::GetInstance()->GetToolManager(); text += toolManager->GetToolById(id)->GetName(); text += "\""; mitk::StatusBar::GetInstance()->DisplayText(text.c_str()); us::ModuleResource resource = toolManager->GetToolById(id)->GetCursorIconResource(); this->SetMouseCursor(resource, 0, 0); } else { this->ResetMouseCursor(); mitk::StatusBar::GetInstance()->DisplayText(""); } } void QmitkSegmentationView::ResetMouseCursor() { if ( m_MouseCursorSet ) { mitk::ApplicationCursor::GetInstance()->PopCursor(); m_MouseCursorSet = false; } } void QmitkSegmentationView::SetMouseCursor( const us::ModuleResource& resource, int hotspotX, int hotspotY ) { // Remove previously set mouse cursor if (m_MouseCursorSet) this->ResetMouseCursor(); if (resource) { us::ModuleResourceStream cursor(resource, std::ios::binary); mitk::ApplicationCursor::GetInstance()->PushCursor(cursor, hotspotX, hotspotY); m_MouseCursorSet = true; } } void QmitkSegmentationView::SetToolSelectionBoxesEnabled(bool status) { if (status) { m_Controls->m_ManualToolSelectionBox2D->RecreateButtons(); m_Controls->m_ManualToolSelectionBox3D->RecreateButtons(); } m_Controls->m_ManualToolSelectionBox2D->setEnabled(status); m_Controls->m_ManualToolSelectionBox3D->setEnabled(status); m_Controls->m_SlicesInterpolator->setEnabled(status); }