From 741d1e0014bb3f7078b6e783a7b1a908dc86e80a Mon Sep 17 00:00:00 2001 From: Jean-Christophe Fillion-Robin Date: Mon, 3 Dec 2018 18:40:19 -0500 Subject: [PATCH] ENH: Support associating view with specific interaction node This commit adds support for adding an interaction node reference to view. By default, all views are implicitly associated with the interaction singleton node. This allows to explicitly associate a subset of the views with a particular interaction node. Widgets expecting to work with an interaction node have also been updated to have interactionNode()/setInteractionNode() functions allowing to override the default interaction node. See https://discourse.slicer.org/t/setting-mouse-place-mode-for-specific-view/4938 --- Base/QTGUI/qSlicerMouseModeToolBar.cxx | 52 ++++++++++++++----- Base/QTGUI/qSlicerMouseModeToolBar.h | 9 ++++ Base/QTGUI/qSlicerMouseModeToolBar_p.h | 2 + .../Core/Testing/vtkMRMLViewNodeTest1.cxx | 31 +++++++++++ Libs/MRML/Core/vtkMRMLAbstractViewNode.cxx | 36 +++++++++++++ Libs/MRML/Core/vtkMRMLAbstractViewNode.h | 28 ++++++++++ .../vtkMRMLAbstractDisplayableManager.cxx | 51 ++++++++++++------ .../vtkSliceViewInteractorStyle.cxx | 9 ++-- .../vtkThreeDViewInteractorStyle.cxx | 20 +++++-- Libs/MRML/Logic/vtkMRMLApplicationLogic.h | 4 +- .../Logic/vtkSlicerAnnotationModuleLogic.cxx | 24 ++++----- .../Logic/vtkSlicerAnnotationModuleLogic.h | 15 +++--- .../Markups/Logic/vtkSlicerMarkupsLogic.cxx | 21 +++++--- .../Markups/Logic/vtkSlicerMarkupsLogic.h | 3 +- .../Testing/Python/MarkupsWidgetsSelfTest.py | 17 +++++- .../Widgets/qSlicerMarkupsPlaceWidget.cxx | 25 ++++++++- .../Widgets/qSlicerMarkupsPlaceWidget.h | 9 ++++ .../Widgets/qSlicerSimpleMarkupsWidget.cxx | 14 +++++ .../Widgets/qSlicerSimpleMarkupsWidget.h | 8 +++ .../Widgets/qMRMLSegmentEditorWidget.cxx | 19 +++++++ .../Widgets/qMRMLSegmentEditorWidget.h | 9 ++++ 21 files changed, 336 insertions(+), 70 deletions(-) diff --git a/Base/QTGUI/qSlicerMouseModeToolBar.cxx b/Base/QTGUI/qSlicerMouseModeToolBar.cxx index 2f5ffe80a..8af6201d6 100644 --- a/Base/QTGUI/qSlicerMouseModeToolBar.cxx +++ b/Base/QTGUI/qSlicerMouseModeToolBar.cxx @@ -123,11 +123,7 @@ void qSlicerMouseModeToolBarPrivate::setMRMLScene(vtkMRMLScene* newScene) this->MRMLScene = newScene; // watch for changes to the interaction, selection nodes so can update the widget - vtkMRMLInteractionNode* interactionNode = - (this->MRMLAppLogic && this->MRMLScene) ? - this->MRMLAppLogic->GetInteractionNode() : 0; - this->qvtkReconnect(interactionNode, vtkCommand::ModifiedEvent, - this, SLOT(updateWidgetFromInteractionNode())); + q->setInteractionNode((this->MRMLAppLogic && this->MRMLScene) ? this->MRMLAppLogic->GetInteractionNode() : 0); vtkMRMLSelectionNode* selectionNode = (this->MRMLAppLogic && this->MRMLScene) ? @@ -249,8 +245,8 @@ void qSlicerMouseModeToolBarPrivate::updateWidgetFromSelectionNode() //--------------------------------------------------------------------------- void qSlicerMouseModeToolBarPrivate::updateWidgetFromInteractionNode() { - vtkMRMLInteractionNode* interactionNode = - this->MRMLAppLogic ? this->MRMLAppLogic->GetInteractionNode() : 0; + Q_Q(qSlicerMouseModeToolBar); + vtkMRMLInteractionNode* interactionNode = q->interactionNode(); if (!interactionNode) { qDebug() << "Mouse Mode ToolBar: no interaction node"; @@ -355,6 +351,8 @@ void qSlicerMouseModeToolBarPrivate::onMRMLSceneEndBatchProcess() // re-enable in case it didn't get re-enabled for scene load q->setEnabled(true); + q->setInteractionNode((this->MRMLAppLogic && this->MRMLScene) ? this->MRMLAppLogic->GetInteractionNode() : 0); + // update the state from mrml this->updateWidgetFromMRML(); } @@ -421,7 +419,7 @@ void qSlicerMouseModeToolBar::switchToViewTransformMode() return; } - vtkMRMLInteractionNode * interactionNode = d->MRMLAppLogic->GetInteractionNode(); + vtkMRMLInteractionNode * interactionNode = this->interactionNode(); if (interactionNode) { // update the interaction node, should trigger a cursor update @@ -469,6 +467,11 @@ void qSlicerMouseModeToolBar::changeCursorTo(QCursor cursor) { return; } + // Update cursor only if view interaction node corresponds to the one associated with the mouse toolbar + if (threeDView->mrmlViewNode()->GetInteractionNode() != this->interactionNode()) + { + continue; + } threeDView->setCursor(cursor); #if VTK_MAJOR_VERSION >= 9 || (VTK_MAJOR_VERSION >= 8 && VTK_MINOR_VERSION >= 2) threeDView->VTKWidget()->setQVTKCursor(cursor); @@ -483,6 +486,11 @@ void qSlicerMouseModeToolBar::changeCursorTo(QCursor cursor) { return; } + // Update cursor only if view interaction node corresponds to the one associated with the mouse toolbar + if (sliceView->mrmlSliceNode()->GetInteractionNode() != this->interactionNode()) + { + continue; + } sliceView->setCursor(cursor); #if VTK_MAJOR_VERSION >= 9 || (VTK_MAJOR_VERSION >= 8 && VTK_MINOR_VERSION >= 2) sliceView->VTKWidget()->setQVTKCursor(cursor); @@ -528,7 +536,7 @@ void qSlicerMouseModeToolBar::switchPlaceMode() QString previousPlaceNodeClassName = QString(selectionNode->GetActivePlaceNodeClassName()); selectionNode->SetReferenceActivePlaceNodeClassName(placeNodeClassName.toLatin1()); // update the interaction mode, which will trigger an update of the cursor - vtkMRMLInteractionNode * interactionNode = d->MRMLAppLogic->GetInteractionNode(); + vtkMRMLInteractionNode * interactionNode = this->interactionNode(); if (interactionNode) { // is this a click on top of a single or persistent place mode? @@ -571,11 +579,7 @@ QAction* qSlicerMouseModeToolBar::actionFromPlaceNodeClassName(QString placeNode //--------------------------------------------------------------------------- void qSlicerMouseModeToolBar::setPersistence(bool persistent) { - Q_D(qSlicerMouseModeToolBar); - - vtkMRMLInteractionNode *interactionNode = - d->MRMLAppLogic ? d->MRMLAppLogic->GetInteractionNode() : 0; - + vtkMRMLInteractionNode *interactionNode = this->interactionNode(); if (interactionNode) { interactionNode->SetPlaceModePersistence(persistent ? 1 : 0); @@ -615,3 +619,23 @@ void qSlicerMouseModeToolBar::setDefaultPlaceClassName(const QString& className) Q_D(qSlicerMouseModeToolBar); d->DefaultPlaceClassName = className; } + +//----------------------------------------------------------------------------- +vtkMRMLInteractionNode* qSlicerMouseModeToolBar::interactionNode()const +{ + Q_D(const qSlicerMouseModeToolBar); + return d->InteractionNode; +} + +//----------------------------------------------------------------------------- +void qSlicerMouseModeToolBar::setInteractionNode(vtkMRMLInteractionNode* interactionNode) +{ + Q_D(qSlicerMouseModeToolBar); + if (d->InteractionNode == interactionNode) + { + return; + } + d->qvtkReconnect(d->InteractionNode, interactionNode, vtkCommand::ModifiedEvent, + d, SLOT(updateWidgetFromInteractionNode())); + d->InteractionNode = interactionNode; +} diff --git a/Base/QTGUI/qSlicerMouseModeToolBar.h b/Base/QTGUI/qSlicerMouseModeToolBar.h index 1a37c3b14..1844708f8 100644 --- a/Base/QTGUI/qSlicerMouseModeToolBar.h +++ b/Base/QTGUI/qSlicerMouseModeToolBar.h @@ -29,6 +29,7 @@ #include "qSlicerBaseQTGUIExport.h" class qSlicerMouseModeToolBarPrivate; +class vtkMRMLInteractionNode; class vtkMRMLScene; class vtkSlicerApplicationLogic; @@ -58,6 +59,10 @@ public: QString defaultPlaceClassName()const; void setDefaultPlaceClassName(const QString& className); + /// Get interaction node. + /// \sa setInteractionNode() + Q_INVOKABLE vtkMRMLInteractionNode* interactionNode()const; + public slots: /// Set the application logic. It is used to retrieve the selection and @@ -77,6 +82,10 @@ public slots: /// Update the interaction node's persistent place mode from the UI void setPersistence(bool persistent); + /// Set interaction node used to update the toolbar. + /// \sa interactionNode() + void setInteractionNode(vtkMRMLInteractionNode* interactionNode); + protected: QScopedPointer d_ptr; diff --git a/Base/QTGUI/qSlicerMouseModeToolBar_p.h b/Base/QTGUI/qSlicerMouseModeToolBar_p.h index c748ce25a..b62cb5565 100644 --- a/Base/QTGUI/qSlicerMouseModeToolBar_p.h +++ b/Base/QTGUI/qSlicerMouseModeToolBar_p.h @@ -53,6 +53,7 @@ // VTK includes #include +#include class qSlicerMouseModeToolBarPrivate; class QAction; @@ -92,6 +93,7 @@ public: vtkSmartPointer MRMLScene; vtkSmartPointer MRMLAppLogic; + vtkWeakPointer InteractionNode; /// PlaceMode button and menu QToolButton *CreateAndPlaceToolButton; diff --git a/Libs/MRML/Core/Testing/vtkMRMLViewNodeTest1.cxx b/Libs/MRML/Core/Testing/vtkMRMLViewNodeTest1.cxx index b56413f70..11a2b19fd 100644 --- a/Libs/MRML/Core/Testing/vtkMRMLViewNodeTest1.cxx +++ b/Libs/MRML/Core/Testing/vtkMRMLViewNodeTest1.cxx @@ -11,11 +11,42 @@ =========================================================================auto=*/ #include "vtkMRMLCoreTestingMacros.h" +#include "vtkMRMLInteractionNode.h" +#include "vtkMRMLScene.h" #include "vtkMRMLViewNode.h" int vtkMRMLViewNodeTest1(int , char * [] ) { vtkNew node1; EXERCISE_ALL_BASIC_MRML_METHODS(node1.GetPointer()); + + // Test Set/GetInteractionNode without scene + { + vtkNew viewNode; + vtkNew interactionNode; + CHECK_NULL(viewNode->GetInteractionNode()); + viewNode->SetInteractionNode(interactionNode.GetPointer()); + CHECK_NULL(viewNode->GetInteractionNode()); + } + + // Test Set/GetInteractionNode with scene + { + vtkNew scene; + vtkNew viewNode; + vtkNew interactionNode; // interaction node is a singleton by default + scene->AddNode(viewNode.GetPointer()); + scene->AddNode(interactionNode.GetPointer()); + CHECK_POINTER(viewNode->GetInteractionNode(), interactionNode.GetPointer()); + CHECK_POINTER(viewNode->GetInteractionNode(), scene->GetNodeByID("vtkMRMLInteractionNodeSingleton")); + + vtkNew otherInteractionNode; + otherInteractionNode->SetSingletonOff(); + scene->AddNode(otherInteractionNode.GetPointer()); + CHECK_POINTER(viewNode->GetInteractionNode(), scene->GetNodeByID("vtkMRMLInteractionNodeSingleton")); + viewNode->SetInteractionNode(otherInteractionNode.GetPointer()); + CHECK_POINTER(viewNode->GetInteractionNode(), otherInteractionNode.GetPointer()); + CHECK_POINTER_DIFFERENT(otherInteractionNode.GetPointer(), scene->GetNodeByID("vtkMRMLInteractionNodeSingleton")); + } + return EXIT_SUCCESS; } diff --git a/Libs/MRML/Core/vtkMRMLAbstractViewNode.cxx b/Libs/MRML/Core/vtkMRMLAbstractViewNode.cxx index 7db383e39..7dd2bac27 100644 --- a/Libs/MRML/Core/vtkMRMLAbstractViewNode.cxx +++ b/Libs/MRML/Core/vtkMRMLAbstractViewNode.cxx @@ -23,6 +23,7 @@ // MRML includes #include "vtkMRMLAbstractViewNode.h" +#include "vtkMRMLInteractionNode.h" #include "vtkMRMLModelNode.h" #include "vtkMRMLScene.h" @@ -31,6 +32,7 @@ const char* vtkMRMLAbstractViewNode::OrientationMarkerHumanModelReferenceRole = "OrientationMarkerHumanModel"; const char* vtkMRMLAbstractViewNode::ParentLayoutNodeReferenceRole = "ParentLayoutNodeRef"; +const char* vtkMRMLAbstractViewNode::InteractionNodeReferenceRole = "InteractionNodeRef"; const int vtkMRMLAbstractViewNode::AxisLabelsCount = 6; static const char* DEFAULT_AXIS_LABELS[vtkMRMLAbstractViewNode::AxisLabelsCount] = {"L", "R", "P", "A", "I", "S"}; @@ -293,6 +295,40 @@ void vtkMRMLAbstractViewNode::PrintSelf(ostream& os, vtkIndent indent) } //------------------------------------------------------------------------------ +vtkMRMLInteractionNode* vtkMRMLAbstractViewNode::GetInteractionNode() +{ + vtkMRMLInteractionNode * interactionNode = + vtkMRMLInteractionNode::SafeDownCast(this->GetNodeReference(this->InteractionNodeReferenceRole)); + if (this->GetScene() && !interactionNode) + { + interactionNode = vtkMRMLInteractionNode::SafeDownCast ( + this->GetScene()->GetNodeByID("vtkMRMLInteractionNodeSingleton")); + } + return interactionNode; +} + +//------------------------------------------------------------------------------ +bool vtkMRMLAbstractViewNode::SetInteractionNodeID(const char *interactionNodeId) +{ + if (!interactionNodeId) + { + return false; + } + this->SetNodeReferenceID(this->InteractionNodeReferenceRole, interactionNodeId); + return true; +} + +//------------------------------------------------------------------------------ +bool vtkMRMLAbstractViewNode::SetInteractionNode(vtkMRMLNode* node) +{ + if (node && this->Scene != node->GetScene()) + { + vtkErrorMacro("Cannot set reference: the referenced and referencing node are not in the same scene"); + return false; + } + return this->SetInteractionNodeID(node ? node->GetID() : NULL); +} + int vtkMRMLAbstractViewNode::IsMappedInLayout() { if (!this->GetAttribute("MappedInLayout")) diff --git a/Libs/MRML/Core/vtkMRMLAbstractViewNode.h b/Libs/MRML/Core/vtkMRMLAbstractViewNode.h index 59c372c10..133c02b9b 100644 --- a/Libs/MRML/Core/vtkMRMLAbstractViewNode.h +++ b/Libs/MRML/Core/vtkMRMLAbstractViewNode.h @@ -27,6 +27,7 @@ // MRML includes #include "vtkMRMLNode.h" +class vtkMRMLInteractionNode; class vtkMRMLModelNode; class vtkStringArray; @@ -98,6 +99,32 @@ public: vtkGetMacro(Visibility, int); vtkSetMacro(Visibility, int); + /// \brief Get interaction node. + /// + /// If no node reference has been explicitly set using SetInteractionNode() + /// or SetInteractionNodeID(), return the singleton interaction node. + /// + /// The singleton interaction node is considered to be the default interaction node. Associating + /// a specific interaction node to one or a multiple views allows to control the interaction mode + /// associated with these views. + /// + /// Since by default, the interaction node is a singleton, a new interaction node may be + /// created doing the following: + /// + /// \code{.cpp} + /// vtkNew interactionNode; + /// interactionNode->SetSingletonOff(); + /// this->GetMRMLScene()->AddNode(interactionNode.GetPointer()); + /// \endcode + /// + /// \sa SetInteractionNodeID() SetInteractionNode() + vtkMRMLInteractionNode* GetInteractionNode(); + /// Set interaction node reference. + /// \sa GetInteractionNode() + bool SetInteractionNodeID(const char *interactionNodeId); + /// Set interaction node reference. + /// \sa GetInteractionNode() + bool SetInteractionNode(vtkMRMLNode* node); /// Indicates whether or not the view is mapped in the current layout. /// \sa GetVisibility() @@ -286,6 +313,7 @@ protected: vtkSmartPointer AxisLabels; static const char* ParentLayoutNodeReferenceRole; + static const char* InteractionNodeReferenceRole; }; //------------------------------------------------------------------------------ diff --git a/Libs/MRML/DisplayableManager/vtkMRMLAbstractDisplayableManager.cxx b/Libs/MRML/DisplayableManager/vtkMRMLAbstractDisplayableManager.cxx index e6529a578..3b0827265 100644 --- a/Libs/MRML/DisplayableManager/vtkMRMLAbstractDisplayableManager.cxx +++ b/Libs/MRML/DisplayableManager/vtkMRMLAbstractDisplayableManager.cxx @@ -27,6 +27,7 @@ #include // MRML includes +#include #include #include #include @@ -532,6 +533,9 @@ vtkMRMLAbstractDisplayableManager::vtkMRMLAbstractDisplayableManager() this->AddObserver(vtkCommand::DeleteEvent, this->Internal->DeleteCallBackCommand); // Default observable event associated with DisplayableNode this->AddMRMLDisplayableManagerEvent(vtkCommand::ModifiedEvent); + this->AddMRMLDisplayableManagerEvent(vtkMRMLNode::ReferenceAddedEvent); + this->AddMRMLDisplayableManagerEvent(vtkMRMLNode::ReferenceRemovedEvent); + this->AddMRMLDisplayableManagerEvent(vtkMRMLNode::ReferenceModifiedEvent); // Setup widgets callback vtkObserverManager * widgetsObserver = this->Internal->WidgetsObserverManager; @@ -577,9 +581,7 @@ void vtkMRMLAbstractDisplayableManager::CreateIfPossible() assert(this->GetMRMLDisplayableNode()); // Look for InteractionNode - this->Internal->MRMLInteractionNode = vtkMRMLInteractionNode::SafeDownCast( - this->GetMRMLScene()->GetNodeByID("vtkMRMLInteractionNodeSingleton")); - if (!this->Internal->MRMLInteractionNode) + if (!this->GetInteractionNode()) { vtkWarningMacro( << "CreateIfPossible - MRMLScene does NOT contain any InteractionNode"); } @@ -699,11 +701,29 @@ int vtkMRMLAbstractDisplayableManager::ActiveInteractionModes() { void vtkMRMLAbstractDisplayableManager::ProcessMRMLNodesEvents( vtkObject* caller, unsigned long event, void * callData) { - if (caller == this->GetMRMLDisplayableNode() && - event == vtkCommand::ModifiedEvent) - { - this->OnMRMLDisplayableNodeModifiedEvent(caller); - return; + if (caller == this->GetMRMLDisplayableNode()) + { + if(event == vtkCommand::ModifiedEvent) + { + this->OnMRMLDisplayableNodeModifiedEvent(caller); + return; + } + else if(event == vtkMRMLNode::ReferenceAddedEvent || + event == vtkMRMLNode::ReferenceRemovedEvent || + event == vtkMRMLNode::ReferenceModifiedEvent) + { + // Update interaction node + vtkMRMLAbstractViewNode* viewNode = vtkMRMLAbstractViewNode::SafeDownCast(this->GetMRMLDisplayableNode()); + if (viewNode) + { + this->Internal->SetAndObserveMRMLInteractionNode(viewNode->GetInteractionNode()); + } + else + { + vtkErrorMacro(<< "ProcessMRMLNodesEvents failed: " + << "No viewNode is associated with the displayable manager: " << this->GetClassName()); + } + } } this->Superclass::ProcessMRMLNodesEvents(caller, event, callData); } @@ -791,17 +811,16 @@ void vtkMRMLAbstractDisplayableManager::SetAndObserveMRMLDisplayableNode( { // Observe scene associated with the MRML DisplayableNode vtkMRMLScene * sceneToObserve = 0; - if (newMRMLDisplayableNode) + vtkMRMLAbstractViewNode* viewNode = vtkMRMLAbstractViewNode::SafeDownCast(newMRMLDisplayableNode); + if (viewNode) { - sceneToObserve = newMRMLDisplayableNode->GetScene(); + sceneToObserve = viewNode->GetScene(); // Observe InteractionNode - vtkMRMLInteractionNode *interactionNode = vtkMRMLInteractionNode::SafeDownCast ( - sceneToObserve->GetNodeByID("vtkMRMLInteractionNodeSingleton")); + vtkMRMLInteractionNode *interactionNode = viewNode->GetInteractionNode(); + this->Internal->SetAndObserveMRMLInteractionNode(interactionNode); if (interactionNode) { - // Observe MRML InteractionNode only if a valid MRML DisplayableNode is set - this->Internal->SetAndObserveMRMLInteractionNode(newMRMLDisplayableNode ? interactionNode : 0); this->Internal->UpdateInteractorStyle(); } else @@ -811,12 +830,12 @@ void vtkMRMLAbstractDisplayableManager::SetAndObserveMRMLDisplayableNode( } } vtkSetAndObserveMRMLNodeEventsMacro(this->Internal->MRMLDisplayableNode, - newMRMLDisplayableNode, + viewNode, this->Internal->MRMLDisplayableNodeObservableEvents); this->SetMRMLScene(sceneToObserve); this->SetUpdateFromMRMLRequested(true); this->CreateIfPossible(); - if (newMRMLDisplayableNode != 0) + if (viewNode != 0) { this->RequestRender(); } diff --git a/Libs/MRML/DisplayableManager/vtkSliceViewInteractorStyle.cxx b/Libs/MRML/DisplayableManager/vtkSliceViewInteractorStyle.cxx index 3e7ad5750..b09758835 100644 --- a/Libs/MRML/DisplayableManager/vtkSliceViewInteractorStyle.cxx +++ b/Libs/MRML/DisplayableManager/vtkSliceViewInteractorStyle.cxx @@ -340,9 +340,12 @@ int vtkSliceViewInteractorStyle::GetMouseInteractionMode() vtkErrorMacro("vtkSliceViewInteractorStyle::GetMouseInteractionMode: failed to get scene"); return vtkMRMLInteractionNode::ViewTransform; } - vtkMRMLScene* scene = this->SliceLogic->GetMRMLScene(); - - vtkMRMLInteractionNode *interactionNode = vtkMRMLInteractionNode::SafeDownCast(scene->GetNodeByID("vtkMRMLInteractionNodeSingleton")); + if ( this->SliceLogic->GetSliceNode() == 0 ) + { + vtkErrorMacro("vtkSliceViewInteractorStyle::GetMouseInteractionMode: failed to get slice view node"); + return vtkMRMLInteractionNode::ViewTransform; + } + vtkMRMLInteractionNode *interactionNode = this->SliceLogic->GetSliceNode()->GetInteractionNode(); if (interactionNode == 0) { vtkErrorMacro("vtkSliceViewInteractorStyle::GetMouseInteractionMode: failed to get interaction node"); diff --git a/Libs/MRML/DisplayableManager/vtkThreeDViewInteractorStyle.cxx b/Libs/MRML/DisplayableManager/vtkThreeDViewInteractorStyle.cxx index e4557b600..227b77305 100644 --- a/Libs/MRML/DisplayableManager/vtkThreeDViewInteractorStyle.cxx +++ b/Libs/MRML/DisplayableManager/vtkThreeDViewInteractorStyle.cxx @@ -325,8 +325,14 @@ void vtkThreeDViewInteractorStyle::OnLeftButtonDown() if ( this->GetCameraNode() != 0 && this->GetCameraNode()->GetScene() != 0 ) { - interactionNode = vtkMRMLInteractionNode::SafeDownCast( - this->GetCameraNode()->GetScene()->GetNodeByID("vtkMRMLInteractionNodeSingleton")); + vtkMRMLAbstractViewNode* viewNode = vtkMRMLAbstractViewNode::SafeDownCast( + this->GetCameraNode()->GetScene()->GetNodeByID(this->GetCameraNode()->GetActiveTag())); + if (!viewNode) + { + vtkErrorMacro("OnLeftButtonDown: failed to lookup view node associated with camera node = " + << this->GetCameraNode()->GetID()); + } + interactionNode = viewNode ? viewNode->GetInteractionNode() : 0; if (interactionNode != 0) { @@ -399,8 +405,14 @@ void vtkThreeDViewInteractorStyle::OnLeftButtonUp() if ( this->GetCameraNode() != 0 && this->GetCameraNode()->GetScene() != 0 ) { - interactionNode = vtkMRMLInteractionNode::SafeDownCast( - this->GetCameraNode()->GetScene()->GetNodeByID("vtkMRMLInteractionNodeSingleton")); + vtkMRMLAbstractViewNode* viewNode = vtkMRMLAbstractViewNode::SafeDownCast( + this->GetCameraNode()->GetScene()->GetNodeByID(this->GetCameraNode()->GetActiveTag())); + if (!viewNode) + { + vtkErrorMacro("OnLeftButtonUp: failed to lookup view node associated with camera node = " + << this->GetCameraNode()->GetID()); + } + interactionNode = viewNode ? viewNode->GetInteractionNode() : 0; if (interactionNode != 0) { diff --git a/Libs/MRML/Logic/vtkMRMLApplicationLogic.h b/Libs/MRML/Logic/vtkMRMLApplicationLogic.h index b803e10ea..d031d3ad9 100644 --- a/Libs/MRML/Logic/vtkMRMLApplicationLogic.h +++ b/Libs/MRML/Logic/vtkMRMLApplicationLogic.h @@ -54,10 +54,10 @@ public: void PrintSelf(ostream& os, vtkIndent indent) VTK_OVERRIDE; vtkTypeMacro(vtkMRMLApplicationLogic, vtkMRMLAbstractLogic); - /// Get current Selection node + /// Get default Selection node vtkMRMLSelectionNode* GetSelectionNode()const; - /// Get current Interaction node + /// Get default Interaction node vtkMRMLInteractionNode* GetInteractionNode()const; /// All the slice logics in the application diff --git a/Modules/Loadable/Annotations/Logic/vtkSlicerAnnotationModuleLogic.cxx b/Modules/Loadable/Annotations/Logic/vtkSlicerAnnotationModuleLogic.cxx index 7a92eb038..c73dc8895 100644 --- a/Modules/Loadable/Annotations/Logic/vtkSlicerAnnotationModuleLogic.cxx +++ b/Modules/Loadable/Annotations/Logic/vtkSlicerAnnotationModuleLogic.cxx @@ -319,7 +319,7 @@ char *vtkSlicerAnnotationModuleLogic::AddFiducial(double r, double a, double s, //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- -void vtkSlicerAnnotationModuleLogic::ProcessMRMLNodesEvents(vtkObject *caller, +void vtkSlicerAnnotationModuleLogic::ProcessMRMLNodesEvents(vtkObject *vtkNotUsed(caller), unsigned long event, void *callData) { @@ -510,11 +510,9 @@ void vtkSlicerAnnotationModuleLogic::AddAnnotationNode(const char * nodeDescript //--------------------------------------------------------------------------- // Start the place mouse mode //--------------------------------------------------------------------------- -void vtkSlicerAnnotationModuleLogic::StartPlaceMode(bool persistent) +void vtkSlicerAnnotationModuleLogic::StartPlaceMode(bool persistent, vtkMRMLInteractionNode* interactionNode) { - - vtkMRMLInteractionNode *interactionNode = NULL; - if ( this->GetMRMLScene()) + if (!interactionNode && this->GetMRMLScene()) { interactionNode = vtkMRMLInteractionNode::SafeDownCast(this->GetMRMLScene()->GetNodeByID("vtkMRMLInteractionNodeSingleton")); } @@ -558,7 +556,7 @@ void vtkSlicerAnnotationModuleLogic::AddNodeCompleted(vtkMRMLAnnotationNode* ann //--------------------------------------------------------------------------- // Exit the place mode //--------------------------------------------------------------------------- -void vtkSlicerAnnotationModuleLogic::StopPlaceMode(bool persistent) +void vtkSlicerAnnotationModuleLogic::StopPlaceMode(bool persistent, vtkMRMLInteractionNode* interactionNode) { vtkMRMLSelectionNode *selectionNode = NULL; @@ -573,9 +571,10 @@ void vtkSlicerAnnotationModuleLogic::StopPlaceMode(bool persistent) return; } - vtkMRMLInteractionNode *interactionNode = - vtkMRMLInteractionNode::SafeDownCast( - this->GetMRMLScene()->GetNodeByID("vtkMRMLInteractionNodeSingleton")); + if (!interactionNode && this->GetMRMLScene()) + { + interactionNode = vtkMRMLInteractionNode::SafeDownCast(this->GetMRMLScene()->GetNodeByID("vtkMRMLInteractionNodeSingleton")); + } if (interactionNode == NULL) { vtkErrorMacro ( "StopPlaceMode: No interaction node in the scene." ); @@ -609,15 +608,12 @@ void vtkSlicerAnnotationModuleLogic::StopPlaceMode(bool persistent) //--------------------------------------------------------------------------- // Cancel the current placement or remove the last placed node //--------------------------------------------------------------------------- -void vtkSlicerAnnotationModuleLogic::CancelCurrentOrRemoveLastAddedAnnotationNode() +void vtkSlicerAnnotationModuleLogic::CancelCurrentOrRemoveLastAddedAnnotationNode(vtkMRMLInteractionNode* interactionNode) { - - vtkMRMLInteractionNode *interactionNode = NULL; - if (this->GetMRMLScene()) + if (!interactionNode && this->GetMRMLScene()) { interactionNode = vtkMRMLInteractionNode::SafeDownCast(this->GetMRMLScene()->GetNodeByID("vtkMRMLInteractionNodeSingleton")); } - if (!interactionNode) { vtkErrorMacro("CancelCurrentOrRemoveLastAddedAnnotationNode: No interaction node") diff --git a/Modules/Loadable/Annotations/Logic/vtkSlicerAnnotationModuleLogic.h b/Modules/Loadable/Annotations/Logic/vtkSlicerAnnotationModuleLogic.h index 4e0640740..9b8235c51 100644 --- a/Modules/Loadable/Annotations/Logic/vtkSlicerAnnotationModuleLogic.h +++ b/Modules/Loadable/Annotations/Logic/vtkSlicerAnnotationModuleLogic.h @@ -28,11 +28,13 @@ public: vtkTypeMacro(vtkSlicerAnnotationModuleLogic,vtkSlicerModuleLogic); virtual void PrintSelf(ostream& os, vtkIndent indent) VTK_OVERRIDE; - // Start the place mode for annotations - void StartPlaceMode(bool persistent=false); + /// Start the place mode for annotations. + /// By default, the singleton interaction node is updated. + void StartPlaceMode(bool persistent=false, vtkMRMLInteractionNode* interactionNode = NULL); - // Exit the place mode for annotations - void StopPlaceMode(bool persistent=false); + /// Exit the place mode for annotations. + /// By default, the singleton interaction node is updated. + void StopPlaceMode(bool persistent=false, vtkMRMLInteractionNode* interactionNode = NULL); // Start adding a new annotation Node void AddAnnotationNode(const char * nodeDescriptor, bool persistent=false); @@ -40,8 +42,9 @@ public: // After a node was added, propagate to widget void AddNodeCompleted(vtkMRMLAnnotationNode* annotationNode); - // Cancel the current annotation placement or remove last annotation node - void CancelCurrentOrRemoveLastAddedAnnotationNode(); + /// Cancel the current annotation placement or remove last annotation node. + /// By default, the singleton interaction node is updated. + void CancelCurrentOrRemoveLastAddedAnnotationNode(vtkMRMLInteractionNode* interactionNode = NULL); /// Remove an AnnotationNode and also its 1-1 IS-A hierarchyNode, if found. void RemoveAnnotationNode(vtkMRMLAnnotationNode* annotationNode); diff --git a/Modules/Loadable/Markups/Logic/vtkSlicerMarkupsLogic.cxx b/Modules/Loadable/Markups/Logic/vtkSlicerMarkupsLogic.cxx index b59a95f96..651e34058 100644 --- a/Modules/Loadable/Markups/Logic/vtkSlicerMarkupsLogic.cxx +++ b/Modules/Loadable/Markups/Logic/vtkSlicerMarkupsLogic.cxx @@ -338,10 +338,15 @@ void vtkSlicerMarkupsLogic::SetActiveListID(vtkMRMLMarkupsNode *markupsNode) if (activePlaceNodeClassName && strcmp(activePlaceNodeClassName, "vtkMRMLMarkupsFiducialNode") == 0) { selectionNode->SetActivePlaceNodeID(NULL); - vtkMRMLInteractionNode *interactionNode = vtkMRMLInteractionNode::SafeDownCast(this->GetMRMLScene()->GetNodeByID("vtkMRMLInteractionNodeSingleton")); - if (interactionNode && interactionNode->GetCurrentInteractionMode() == vtkMRMLInteractionNode::Place) + vtkSmartPointer interactionNodes = vtkSmartPointer::Take + (this->GetMRMLScene()->GetNodesByClass("vtkMRMLInteractionNode")); + for(int interactionNodeIndex = 0; interactionNodeIndex < interactionNodes->GetNumberOfItems(); ++interactionNodeIndex) { - interactionNode->SetCurrentInteractionMode(vtkMRMLInteractionNode::ViewTransform); + vtkMRMLInteractionNode *interactionNode = vtkMRMLInteractionNode::SafeDownCast(interactionNodes->GetItemAsObject(interactionNodeIndex)); + if (interactionNode->GetCurrentInteractionMode() == vtkMRMLInteractionNode::Place) + { + interactionNode->SetCurrentInteractionMode(vtkMRMLInteractionNode::ViewTransform); + } } } return; @@ -1298,7 +1303,7 @@ void vtkSlicerMarkupsLogic::RenameAllMarkupsFromCurrentFormat(vtkMRMLMarkupsNode } //--------------------------------------------------------------------------- -bool vtkSlicerMarkupsLogic::StartPlaceMode(bool persistent) +bool vtkSlicerMarkupsLogic::StartPlaceMode(bool persistent, vtkMRMLInteractionNode* interactionNode) { if (!this->GetMRMLScene()) { @@ -1318,9 +1323,11 @@ bool vtkSlicerMarkupsLogic::StartPlaceMode(bool persistent) selectionNode->SetReferenceActivePlaceNodeClassName("vtkMRMLMarkupsFiducialNode"); // now go into place mode with the persistece flag set - vtkMRMLInteractionNode *interactionNode = - vtkMRMLInteractionNode::SafeDownCast( - this->GetMRMLScene()->GetNodeByID("vtkMRMLInteractionNodeSingleton")); + if (!interactionNode) + { + interactionNode = vtkMRMLInteractionNode::SafeDownCast( + this->GetMRMLScene()->GetNodeByID("vtkMRMLInteractionNodeSingleton")); + } if (!interactionNode) { vtkErrorMacro ("StartPlaceMode: No interaction node in the scene." ); diff --git a/Modules/Loadable/Markups/Logic/vtkSlicerMarkupsLogic.h b/Modules/Loadable/Markups/Logic/vtkSlicerMarkupsLogic.h index 21f47ef7d..e5d0c1d0f 100644 --- a/Modules/Loadable/Markups/Logic/vtkSlicerMarkupsLogic.h +++ b/Modules/Loadable/Markups/Logic/vtkSlicerMarkupsLogic.h @@ -185,8 +185,9 @@ public: /// Put the interaction node into place mode, and set the persistence of /// place mode according to the persistent flag. /// Return true on successfully going into place mode, false otherwise. + /// By default, the default interaction node is updated. /// \sa SetActiveIDList - bool StartPlaceMode(bool persistent); + bool StartPlaceMode(bool persistent, vtkMRMLInteractionNode* interactionNode = NULL); /// Inspect all the slice composite nodes in the scene. Return 1 if all have /// SliceIntersectionVisibility set to true, 0 if all have it set to false, diff --git a/Modules/Loadable/Markups/Widgets/Testing/Python/MarkupsWidgetsSelfTest.py b/Modules/Loadable/Markups/Widgets/Testing/Python/MarkupsWidgetsSelfTest.py index 05f2f0953..93baefd34 100644 --- a/Modules/Loadable/Markups/Widgets/Testing/Python/MarkupsWidgetsSelfTest.py +++ b/Modules/Loadable/Markups/Widgets/Testing/Python/MarkupsWidgetsSelfTest.py @@ -107,7 +107,7 @@ class MarkupsWidgetsSelfTestTest(ScriptedLoadableModuleTest): simpleMarkupsWidget = slicer.qSlicerSimpleMarkupsWidget() nodeSelector = slicer.util.findChildren(simpleMarkupsWidget,"MarkupsFiducialNodeComboBox")[0] - + self.assertIsNone(simpleMarkupsWidget.interactionNode()) simpleMarkupsWidget.setMRMLScene(slicer.mrmlScene) simpleMarkupsWidget.show() @@ -157,11 +157,19 @@ class MarkupsWidgetsSelfTestTest(ScriptedLoadableModuleTest): tableWidget = simpleMarkupsWidget.tableWidget() self.assertEqual(tableWidget.rowCount, numberOfFiducialsAdded) + self.assertEqual(simpleMarkupsWidget.interactionNode(), slicer.app.applicationLogic().GetInteractionNode()) + otherInteractionNode = slicer.vtkMRMLInteractionNode() + otherInteractionNode.SetSingletonOff() + slicer.mrmlScene.AddNode(otherInteractionNode) + simpleMarkupsWidget.setInteractionNode(otherInteractionNode) + self.assertEqual(simpleMarkupsWidget.interactionNode(), otherInteractionNode) + # ------------------------------------------------------------------------------ def section_MarkupsPlaceWidget(self): self.delayDisplay("Test MarkupsPlaceWidget",self.delayMs) placeWidget = slicer.qSlicerMarkupsPlaceWidget() + self.assertIsNone(placeWidget.interactionNode()) placeWidget.setMRMLScene(slicer.mrmlScene) placeWidget.setCurrentNode(self.markupsNode1) placeWidget.show() @@ -189,3 +197,10 @@ class MarkupsWidgetsSelfTestTest(ScriptedLoadableModuleTest): placeWidget.placeModeEnabled = False placeWidget.placeModeEnabled = True self.assertTrue(placeWidget.placeModePersistency) + + self.assertEqual(placeWidget.interactionNode(), slicer.app.applicationLogic().GetInteractionNode()) + otherInteractionNode = slicer.vtkMRMLInteractionNode() + otherInteractionNode.SetSingletonOff() + slicer.mrmlScene.AddNode(otherInteractionNode) + placeWidget.setInteractionNode(otherInteractionNode) + self.assertEqual(placeWidget.interactionNode(), otherInteractionNode) diff --git a/Modules/Loadable/Markups/Widgets/qSlicerMarkupsPlaceWidget.cxx b/Modules/Loadable/Markups/Widgets/qSlicerMarkupsPlaceWidget.cxx index e79b77f91..e8d4a65c7 100644 --- a/Modules/Loadable/Markups/Widgets/qSlicerMarkupsPlaceWidget.cxx +++ b/Modules/Loadable/Markups/Widgets/qSlicerMarkupsPlaceWidget.cxx @@ -104,6 +104,7 @@ qSlicerMarkupsPlaceWidget::qSlicerMarkupsPlaceWidget(QWidget* parentWidget) : Su qSlicerMarkupsPlaceWidget::~qSlicerMarkupsPlaceWidget() { this->setCurrentNode(NULL); + this->setInteractionNode(NULL); } //----------------------------------------------------------------------------- @@ -180,6 +181,25 @@ vtkMRMLMarkupsFiducialNode* qSlicerMarkupsPlaceWidget::currentMarkupsFiducialNod return d->CurrentMarkupsNode; } +//----------------------------------------------------------------------------- +vtkMRMLInteractionNode* qSlicerMarkupsPlaceWidget::interactionNode()const +{ + Q_D(const qSlicerMarkupsPlaceWidget); + return d->InteractionNode; +} + +//----------------------------------------------------------------------------- +void qSlicerMarkupsPlaceWidget::setInteractionNode(vtkMRMLInteractionNode* interactionNode) +{ + Q_D(qSlicerMarkupsPlaceWidget); + if (d->InteractionNode == interactionNode) + { + return; + } + this->qvtkReconnect(d->InteractionNode, interactionNode, vtkCommand::ModifiedEvent, this, SLOT(updateWidget())); + d->InteractionNode = interactionNode; +} + //----------------------------------------------------------------------------- void qSlicerMarkupsPlaceWidget::setCurrentNode(vtkMRMLNode* currentNode) { @@ -448,10 +468,11 @@ void qSlicerMarkupsPlaceWidget::setMRMLScene(vtkMRMLScene* scene) interactionNode = vtkMRMLInteractionNode::SafeDownCast( d->MarkupsLogic->GetMRMLScene()->GetNodeByID( "vtkMRMLInteractionNodeSingleton" ) ); } + this->setInteractionNode(interactionNode); + this->qvtkReconnect(d->SelectionNode, selectionNode, vtkCommand::ModifiedEvent, this, SLOT(updateWidget())); - this->qvtkReconnect(d->InteractionNode, interactionNode, vtkCommand::ModifiedEvent, this, SLOT(updateWidget())); d->SelectionNode = selectionNode; - d->InteractionNode = interactionNode; + this->updateWidget(); } diff --git a/Modules/Loadable/Markups/Widgets/qSlicerMarkupsPlaceWidget.h b/Modules/Loadable/Markups/Widgets/qSlicerMarkupsPlaceWidget.h index 56386f95e..0c7487c8d 100644 --- a/Modules/Loadable/Markups/Widgets/qSlicerMarkupsPlaceWidget.h +++ b/Modules/Loadable/Markups/Widgets/qSlicerMarkupsPlaceWidget.h @@ -32,6 +32,7 @@ class qSlicerMarkupsPlaceWidgetPrivate; +class vtkMRMLInteractionNode; class vtkMRMLMarkupsFiducialNode; /// \ingroup Slicer_QtModules_CreateModels @@ -68,6 +69,10 @@ public: Q_INVOKABLE vtkMRMLMarkupsFiducialNode* currentMarkupsFiducialNode() const; + /// Get interaction node. + /// \sa setInteractionNode() + Q_INVOKABLE vtkMRMLInteractionNode* interactionNode()const; + /// Returns true if the current markups node is the active markups node in the scene. bool currentNodeActive() const; @@ -106,6 +111,10 @@ public slots: /// Set the currently selected markups node to be the active markups node in the Slicer scene. Does not change place mode. void setCurrentNodeActive(bool active); + /// Set interaction node used to update the widget. + /// \sa interactionNode() + void setInteractionNode(vtkMRMLInteractionNode* interactionNode); + void setDefaultNodeColor(QColor color); void setNodeColor(QColor color); diff --git a/Modules/Loadable/Markups/Widgets/qSlicerSimpleMarkupsWidget.cxx b/Modules/Loadable/Markups/Widgets/qSlicerSimpleMarkupsWidget.cxx index a5196cc1e..603151b32 100644 --- a/Modules/Loadable/Markups/Widgets/qSlicerSimpleMarkupsWidget.cxx +++ b/Modules/Loadable/Markups/Widgets/qSlicerSimpleMarkupsWidget.cxx @@ -192,6 +192,20 @@ void qSlicerSimpleMarkupsWidget::setCurrentNode(vtkMRMLNode* currentNode) emit markupsFiducialNodeChanged(); } +//----------------------------------------------------------------------------- +vtkMRMLInteractionNode* qSlicerSimpleMarkupsWidget::interactionNode()const +{ + Q_D(const qSlicerSimpleMarkupsWidget); + return d->MarkupsPlaceWidget->interactionNode(); +} + +//----------------------------------------------------------------------------- +void qSlicerSimpleMarkupsWidget::setInteractionNode(vtkMRMLInteractionNode* interactionNode) +{ + Q_D(qSlicerSimpleMarkupsWidget); + d->MarkupsPlaceWidget->setInteractionNode(interactionNode); +} + //----------------------------------------------------------------------------- void qSlicerSimpleMarkupsWidget::setNodeBaseName(QString newNodeBaseName) { diff --git a/Modules/Loadable/Markups/Widgets/qSlicerSimpleMarkupsWidget.h b/Modules/Loadable/Markups/Widgets/qSlicerSimpleMarkupsWidget.h index efee1be92..26da92012 100644 --- a/Modules/Loadable/Markups/Widgets/qSlicerSimpleMarkupsWidget.h +++ b/Modules/Loadable/Markups/Widgets/qSlicerSimpleMarkupsWidget.h @@ -57,6 +57,10 @@ public: /// Deprecated. Use currentNode() instead. Q_INVOKABLE vtkMRMLNode* getCurrentNode(); + /// Get interaction node. + /// \sa setInteractionNode() + Q_INVOKABLE vtkMRMLInteractionNode* interactionNode()const; + /// Get the markups table widget Q_INVOKABLE QTableWidget* tableWidget() const; @@ -102,6 +106,10 @@ public slots: /// Set the default name of the markups node created in the combo box. void setNodeBaseName(QString newNodeBaseName); + /// Set interaction node used to update the widget. + /// \sa interactionNode() + void setInteractionNode(vtkMRMLInteractionNode* interactionNode); + /// Accessors to control place mode behavior void setEnterPlaceModeOnNodeChange(bool); diff --git a/Modules/Loadable/Segmentations/Widgets/qMRMLSegmentEditorWidget.cxx b/Modules/Loadable/Segmentations/Widgets/qMRMLSegmentEditorWidget.cxx index 9d30f9933..9e305b5d0 100644 --- a/Modules/Loadable/Segmentations/Widgets/qMRMLSegmentEditorWidget.cxx +++ b/Modules/Loadable/Segmentations/Widgets/qMRMLSegmentEditorWidget.cxx @@ -1761,6 +1761,25 @@ void qMRMLSegmentEditorWidget::setMRMLSegmentEditorNode(vtkMRMLSegmentEditorNode this->updateWidgetFromMRML(); } +//----------------------------------------------------------------------------- +vtkMRMLInteractionNode* qMRMLSegmentEditorWidget::interactionNode()const +{ + Q_D(const qMRMLSegmentEditorWidget); + return d->InteractionNode; +} + +//----------------------------------------------------------------------------- +void qMRMLSegmentEditorWidget::setInteractionNode(vtkMRMLInteractionNode* interactionNode) +{ + Q_D(qMRMLSegmentEditorWidget); + if (d->InteractionNode == interactionNode) + { + return; + } + this->qvtkReconnect(d->InteractionNode, interactionNode, vtkCommand::ModifiedEvent, this, SLOT(onInteractionNodeModified())); + d->InteractionNode = interactionNode; +} + //------------------------------------------------------------------------------ void qMRMLSegmentEditorWidget::initializeParameterSetNode() { diff --git a/Modules/Loadable/Segmentations/Widgets/qMRMLSegmentEditorWidget.h b/Modules/Loadable/Segmentations/Widgets/qMRMLSegmentEditorWidget.h index ebbc73822..68a892033 100644 --- a/Modules/Loadable/Segmentations/Widgets/qMRMLSegmentEditorWidget.h +++ b/Modules/Loadable/Segmentations/Widgets/qMRMLSegmentEditorWidget.h @@ -39,6 +39,7 @@ #include class vtkMRMLNode; +class vtkMRMLInteractionNode; class vtkMRMLSegmentationNode; class vtkMRMLSegmentEditorNode; class vtkMRMLVolumeNode; @@ -189,6 +190,10 @@ public: Q_INVOKABLE void masterVolumeNodeSelectorRemoveAttribute(const QString& nodeType, const QString& attributeName); + /// Get current interaction node. + /// \sa SetInteractionNode() + Q_INVOKABLE vtkMRMLInteractionNode* interactionNode() const; + public slots: /// Set the MRML \a scene associated with the widget virtual void setMRMLScene(vtkMRMLScene* newScene); @@ -286,6 +291,10 @@ public slots: /// labelmap representation axes. void rotateSliceViewsToSegmentation(); + /// Set node used to notify active effect about interaction node changes. + /// \sa interactionNode() + void setInteractionNode(vtkMRMLInteractionNode* interactionNode); + signals: /// Emitted if different segment is selected in the segment list. void currentSegmentIDChanged(const QString&); -- GitLab