diff --git a/CMakeLists.txt b/CMakeLists.txt index 54d36ae..776152f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -57,6 +57,8 @@ endif() #----------------------------------------------------------------------------- # Extension modules add_subdirectory(VirtualReality) +add_subdirectory(GUIWidgets) +add_subdirectory(PointerSimulator) ## NEXT_MODULE #----------------------------------------------------------------------------- diff --git a/GUIWidgets/CMakeLists.txt b/GUIWidgets/CMakeLists.txt new file mode 100644 index 0000000..c5dc5a0 --- /dev/null +++ b/GUIWidgets/CMakeLists.txt @@ -0,0 +1,81 @@ + +#----------------------------------------------------------------------------- +set(MODULE_NAME GUIWidgets) +set(MODULE_TITLE ${MODULE_NAME}) + +string(TOUPPER ${MODULE_NAME} MODULE_NAME_UPPER) + +#----------------------------------------------------------------------------- +add_subdirectory(MRML) +add_subdirectory(Logic) +add_subdirectory(VTKWidgets) +# add_subdirectory(MRMLDM) +# add_subdirectory(Widgets) + +#----------------------------------------------------------------------------- +set(MODULE_EXPORT_DIRECTIVE "Q_SLICER_QTMODULES_${MODULE_NAME_UPPER}_EXPORT") + +# Current_{source,binary} and Slicer_{Libs,Base} already included +set(MODULE_INCLUDE_DIRECTORIES + ${CMAKE_CURRENT_SOURCE_DIR}/MRML + ${CMAKE_CURRENT_BINARY_DIR}/MRML + ${CMAKE_CURRENT_SOURCE_DIR}/Logic + ${CMAKE_CURRENT_BINARY_DIR}/Logic + ${CMAKE_CURRENT_SOURCE_DIR}/VTKWidgets + ${CMAKE_CURRENT_BINARY_DIR}/VTKWidgets + # ${CMAKE_CURRENT_SOURCE_DIR}/Widgets + # ${CMAKE_CURRENT_BINARY_DIR}/Widgets + ${qSlicerMarkupsModuleWidgets_INCLUDE_DIRS} + ${qSlicerVirtualRealityModuleWidgets_INCLUDE_DIRS} + ) + +set(MODULE_SRCS + qSlicer${MODULE_NAME}Module.cxx + qSlicer${MODULE_NAME}Module.h + qSlicer${MODULE_NAME}ModuleWidget.cxx + qSlicer${MODULE_NAME}ModuleWidget.h + ) + +set(MODULE_MOC_SRCS + qSlicer${MODULE_NAME}Module.h + qSlicer${MODULE_NAME}ModuleWidget.h + ) + +set(MODULE_UI_SRCS + Resources/UI/qSlicer${MODULE_NAME}ModuleWidget.ui + ) + +set(MODULE_TARGET_LIBRARIES + vtkSlicer${MODULE_NAME}ModuleMRML + vtkSlicer${MODULE_NAME}ModuleLogic + vtkSlicer${MODULE_NAME}ModuleVTKWidgets + # vtkSlicer${MODULE_NAME}ModuleMRMLDisplayableManager + # qSlicer${MODULE_NAME}ModuleWidgets + vtkSlicerMarkupsModuleMRMLDisplayableManager + qSlicerMarkupsModuleWidgets + qSlicerVirtualRealityModuleWidgets + vtkSlicerVirtualRealityModuleLogic + ) + +set(MODULE_RESOURCES + Resources/qSlicer${MODULE_NAME}Module.qrc + ) + +#----------------------------------------------------------------------------- +slicerMacroBuildLoadableModule( + NAME ${MODULE_NAME} + TITLE ${MODULE_TITLE} + EXPORT_DIRECTIVE ${MODULE_EXPORT_DIRECTIVE} + INCLUDE_DIRECTORIES ${MODULE_INCLUDE_DIRECTORIES} + SRCS ${MODULE_SRCS} + MOC_SRCS ${MODULE_MOC_SRCS} + UI_SRCS ${MODULE_UI_SRCS} + TARGET_LIBRARIES ${MODULE_TARGET_LIBRARIES} + RESOURCES ${MODULE_RESOURCES} + WITH_GENERIC_TESTS + ) + +#----------------------------------------------------------------------------- +if(BUILD_TESTING) + add_subdirectory(Testing) +endif() diff --git a/GUIWidgets/Logic/CMakeLists.txt b/GUIWidgets/Logic/CMakeLists.txt new file mode 100644 index 0000000..4554569 --- /dev/null +++ b/GUIWidgets/Logic/CMakeLists.txt @@ -0,0 +1,27 @@ +project(vtkSlicer${MODULE_NAME}ModuleLogic) + +set(KIT ${PROJECT_NAME}) + +set(${KIT}_EXPORT_DIRECTIVE "VTK_SLICER_${MODULE_NAME_UPPER}_MODULE_LOGIC_EXPORT") + +set(${KIT}_INCLUDE_DIRECTORIES + ) + +set(${KIT}_SRCS + vtkSlicer${MODULE_NAME}Logic.cxx + vtkSlicer${MODULE_NAME}Logic.h + ) + +set(${KIT}_TARGET_LIBRARIES + vtkSlicer${MODULE_NAME}ModuleMRML + vtkSlicerMarkupsModuleLogic + ) + +#----------------------------------------------------------------------------- +SlicerMacroBuildModuleLogic( + NAME ${KIT} + EXPORT_DIRECTIVE ${${KIT}_EXPORT_DIRECTIVE} + INCLUDE_DIRECTORIES ${${KIT}_INCLUDE_DIRECTORIES} + SRCS ${${KIT}_SRCS} + TARGET_LIBRARIES ${${KIT}_TARGET_LIBRARIES} + ) diff --git a/GUIWidgets/Logic/vtkSlicerGUIWidgetsLogic.cxx b/GUIWidgets/Logic/vtkSlicerGUIWidgetsLogic.cxx new file mode 100644 index 0000000..15ebef8 --- /dev/null +++ b/GUIWidgets/Logic/vtkSlicerGUIWidgetsLogic.cxx @@ -0,0 +1,143 @@ +/*============================================================================== + + Program: 3D Slicer + + Portions (c) Copyright Brigham and Women's Hospital (BWH) All Rights Reserved. + + See COPYRIGHT.txt + or http://www.slicer.org/copyright/copyright.txt for details. + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +==============================================================================*/ + +// GUIWidgets Logic includes +#include "vtkSlicerGUIWidgetsLogic.h" + +// GUIWidgets MRML includes +#include "vtkMRMLGUIWidgetNode.h" +#include "vtkMRMLGUIWidgetDisplayNode.h" + +// MRML includes +#include +#include + +// Markups logic includes +#include + +// VTK includes +#include +#include +#include + +// STD includes +#include + +//---------------------------------------------------------------------------- +vtkStandardNewMacro(vtkSlicerGUIWidgetsLogic); + +//---------------------------------------------------------------------------- +vtkSlicerGUIWidgetsLogic::vtkSlicerGUIWidgetsLogic() +{ +} + +//---------------------------------------------------------------------------- +vtkSlicerGUIWidgetsLogic::~vtkSlicerGUIWidgetsLogic() +{ +} + +//---------------------------------------------------------------------------- +void vtkSlicerGUIWidgetsLogic::PrintSelf(ostream& os, vtkIndent indent) +{ + this->Superclass::PrintSelf(os, indent); +} + +//--------------------------------------------------------------------------- +void vtkSlicerGUIWidgetsLogic::SetMRMLSceneInternal(vtkMRMLScene * newScene) +{ + vtkNew events; + events->InsertNextValue(vtkMRMLScene::NodeAddedEvent); + events->InsertNextValue(vtkMRMLScene::NodeRemovedEvent); + events->InsertNextValue(vtkMRMLScene::EndBatchProcessEvent); + this->SetAndObserveMRMLSceneEventsInternal(newScene, events.GetPointer()); +} + +//--------------------------------------------------------------------------- +void vtkSlicerGUIWidgetsLogic::ObserveMRMLScene() +{ + if (!this->GetMRMLScene()) + { + return; + } + + vtkMRMLApplicationLogic* mrmlAppLogic = this->GetMRMLApplicationLogic(); + if (!mrmlAppLogic) + { + vtkErrorMacro("ObserveMRMLScene: invalid MRML Application Logic"); + return; + } + + vtkMRMLNode* node = this->GetMRMLScene()->GetNodeByID(this->GetSelectionNodeID().c_str()); + if (!node) + { + vtkErrorMacro("Observe MRMLScene: invalid Selection Node"); + return; + } + + // add known markup types to the selection node + vtkMRMLSelectionNode* selectionNode = vtkMRMLSelectionNode::SafeDownCast(node); + if (selectionNode) + { + // got into batch process mode so that an update on the mouse mode tool + // bar is triggered when leave it + this->GetMRMLScene()->StartState(vtkMRMLScene::BatchProcessState); + + auto guiWidgetNode = vtkSmartPointer::New(); + selectionNode->AddNewPlaceNodeClassNameToList( + guiWidgetNode->GetClassName(), guiWidgetNode->GetAddIcon(), guiWidgetNode->GetMarkupType()); + + // trigger an update on the mouse mode toolbar + this->GetMRMLScene()->EndState(vtkMRMLScene::BatchProcessState); + } + + this->Superclass::ObserveMRMLScene(); +} + +//----------------------------------------------------------------------------- +void vtkSlicerGUIWidgetsLogic::RegisterNodes() +{ + vtkMRMLScene* scene = this->GetMRMLScene(); + if (!scene) + { + vtkErrorMacro("RegisterNodes: Invalid MRML scene"); + return; + } + if (!scene->IsNodeClassRegistered("vtkMRMLGUIWidgetNode")) + { + scene->RegisterNodeClass(vtkSmartPointer::New()); + } + if (!scene->IsNodeClassRegistered("vtkMRMLGUIWidgetDisplayNode")) + { + scene->RegisterNodeClass(vtkSmartPointer::New()); + } +} + +//--------------------------------------------------------------------------- +void vtkSlicerGUIWidgetsLogic::UpdateFromMRMLScene() +{ + assert(this->GetMRMLScene() != 0); +} + +//--------------------------------------------------------------------------- +void vtkSlicerGUIWidgetsLogic::OnMRMLSceneNodeAdded(vtkMRMLNode* vtkNotUsed(node)) +{ +} + +//--------------------------------------------------------------------------- +void vtkSlicerGUIWidgetsLogic::OnMRMLSceneNodeRemoved(vtkMRMLNode* vtkNotUsed(node)) +{ +} diff --git a/GUIWidgets/Logic/vtkSlicerGUIWidgetsLogic.h b/GUIWidgets/Logic/vtkSlicerGUIWidgetsLogic.h new file mode 100644 index 0000000..94be264 --- /dev/null +++ b/GUIWidgets/Logic/vtkSlicerGUIWidgetsLogic.h @@ -0,0 +1,63 @@ +/*============================================================================== + + Program: 3D Slicer + + Portions (c) Copyright Brigham and Women's Hospital (BWH) All Rights Reserved. + + See COPYRIGHT.txt + or http://www.slicer.org/copyright/copyright.txt for details. + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +==============================================================================*/ + +// .NAME vtkSlicerGUIWidgetsLogic - slicer logic class for volumes manipulation +// .SECTION Description +// This class manages the logic associated with reading, saving, +// and changing propertied of the volumes + + +#ifndef __vtkSlicerGUIWidgetsLogic_h +#define __vtkSlicerGUIWidgetsLogic_h + +// Slicer includes +#include + +// MRML includes + +#include "vtkSlicerGUIWidgetsModuleLogicExport.h" + +/// \ingroup Slicer_QtModules_ExtensionTemplate +class VTK_SLICER_GUIWIDGETS_MODULE_LOGIC_EXPORT vtkSlicerGUIWidgetsLogic : + public vtkSlicerMarkupsLogic +{ +public: + + static vtkSlicerGUIWidgetsLogic *New(); + vtkTypeMacro(vtkSlicerGUIWidgetsLogic, vtkSlicerMarkupsLogic); + void PrintSelf(ostream& os, vtkIndent indent); + +protected: + vtkSlicerGUIWidgetsLogic(); + virtual ~vtkSlicerGUIWidgetsLogic(); + + /// Initialize listening to MRML events + virtual void SetMRMLSceneInternal(vtkMRMLScene* newScene); + void ObserveMRMLScene() override; + + /// Register MRML Node classes to Scene. Gets called automatically when the MRMLScene is attached to this logic class. + virtual void RegisterNodes(); + virtual void UpdateFromMRMLScene(); + virtual void OnMRMLSceneNodeAdded(vtkMRMLNode* node); + virtual void OnMRMLSceneNodeRemoved(vtkMRMLNode* node); +private: + + vtkSlicerGUIWidgetsLogic(const vtkSlicerGUIWidgetsLogic&); // Not implemented + void operator=(const vtkSlicerGUIWidgetsLogic&); // Not implemented +}; + +#endif diff --git a/GUIWidgets/MRML/CMakeLists.txt b/GUIWidgets/MRML/CMakeLists.txt new file mode 100644 index 0000000..a7b4fcc --- /dev/null +++ b/GUIWidgets/MRML/CMakeLists.txt @@ -0,0 +1,29 @@ +project(vtkSlicer${MODULE_NAME}ModuleMRML) + +set(KIT ${PROJECT_NAME}) + +set(${KIT}_EXPORT_DIRECTIVE "VTK_SLICER_${MODULE_NAME_UPPER}_MODULE_MRML_EXPORT") + +set(${KIT}_INCLUDE_DIRECTORIES + ${vtkSlicer${MODULE_NAME}ModuleVTKWidgets_SOURCE_DIR} + ${vtkSlicer${MODULE_NAME}ModuleVTKWidgets_BINARY_DIR} + ) + +set(${KIT}_SRCS + vtkMRMLGUIWidgetNode.cxx + vtkMRMLGUIWidgetDisplayNode.cxx + ) + +set(${KIT}_TARGET_LIBRARIES + ${MRML_LIBRARIES} + vtkSlicerMarkupsModuleMRML + ) + +#----------------------------------------------------------------------------- +SlicerMacroBuildModuleMRML( + NAME ${KIT} + EXPORT_DIRECTIVE ${${KIT}_EXPORT_DIRECTIVE} + INCLUDE_DIRECTORIES ${${KIT}_INCLUDE_DIRECTORIES} + SRCS ${${KIT}_SRCS} + TARGET_LIBRARIES ${${KIT}_TARGET_LIBRARIES} + ) diff --git a/GUIWidgets/MRML/vtkMRMLGUIWidgetDisplayNode.cxx b/GUIWidgets/MRML/vtkMRMLGUIWidgetDisplayNode.cxx new file mode 100644 index 0000000..2c6a73d --- /dev/null +++ b/GUIWidgets/MRML/vtkMRMLGUIWidgetDisplayNode.cxx @@ -0,0 +1,35 @@ +/*============================================================================== + + Program: 3D Slicer + + Portions (c) Copyright Brigham and Women's Hospital (BWH) All Rights Reserved. + + See COPYRIGHT.txt + or http://www.slicer.org/copyright/copyright.txt for details. + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + This file was originally developed by Csaba Pinter, EBATINCA, S.L., and + development was supported by "ICEX Espana Exportacion e Inversiones" under + the program "Inversiones de Empresas Extranjeras en Actividades de I+D + (Fondo Tecnologico)- Convocatoria 2021" + +==============================================================================*/ + +// MRML includes +#include "vtkMRMLGUIWidgetDisplayNode.h" + +//---------------------------------------------------------------------------- +vtkMRMLNodeNewMacro(vtkMRMLGUIWidgetDisplayNode); + +//---------------------------------------------------------------------------- +vtkMRMLGUIWidgetDisplayNode::vtkMRMLGUIWidgetDisplayNode() +{ +} + +//---------------------------------------------------------------------------- +vtkMRMLGUIWidgetDisplayNode::~vtkMRMLGUIWidgetDisplayNode() = default; diff --git a/GUIWidgets/MRML/vtkMRMLGUIWidgetDisplayNode.h b/GUIWidgets/MRML/vtkMRMLGUIWidgetDisplayNode.h new file mode 100644 index 0000000..9e09bf8 --- /dev/null +++ b/GUIWidgets/MRML/vtkMRMLGUIWidgetDisplayNode.h @@ -0,0 +1,59 @@ +/*============================================================================== + + Program: 3D Slicer + + Portions (c) Copyright Brigham and Women's Hospital (BWH) All Rights Reserved. + + See COPYRIGHT.txt + or http://www.slicer.org/copyright/copyright.txt for details. + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + This file was originally developed by Csaba Pinter, EBATINCA, S.L., and + development was supported by "ICEX Espana Exportacion e Inversiones" under + the program "Inversiones de Empresas Extranjeras en Actividades de I+D + (Fondo Tecnologico)- Convocatoria 2021" + +==============================================================================*/ + +// .NAME vtkMRMLGUIWidgetDisplayNode - MRML node to represent display properties for GUI widget +// .SECTION Description +// Adjusts default display parameters for ROI such as fill opacity. +// + +#ifndef __vtkMRMLGUIWidgetDisplayNode_h +#define __vtkMRMLGUIWidgetDisplayNode_h + +#include "vtkSlicerGUIWidgetsModuleMRMLExport.h" +#include "vtkMRMLMarkupsDisplayNode.h" + +class VTK_SLICER_GUIWIDGETS_MODULE_MRML_EXPORT vtkMRMLGUIWidgetDisplayNode : public vtkMRMLMarkupsDisplayNode +{ +public: + static vtkMRMLGUIWidgetDisplayNode *New(); + vtkTypeMacro(vtkMRMLGUIWidgetDisplayNode, vtkMRMLMarkupsDisplayNode); + + //-------------------------------------------------------------------------- + // MRMLNode methods + //-------------------------------------------------------------------------- + + vtkMRMLNode* CreateNodeInstance ( ) override; + + // Get node XML tag name (like Volume, Markups) + const char* GetNodeTagName() override {return "GUIWidgetDisplay";}; + + /// Copy node content (excludes basic data, such as name and node references). + /// \sa vtkMRMLNode::CopyContent + vtkMRMLCopyContentDefaultMacro(vtkMRMLGUIWidgetDisplayNode); + +protected: + vtkMRMLGUIWidgetDisplayNode(); + ~vtkMRMLGUIWidgetDisplayNode() override; + vtkMRMLGUIWidgetDisplayNode( const vtkMRMLGUIWidgetDisplayNode& ); + void operator= ( const vtkMRMLGUIWidgetDisplayNode& ); +}; +#endif diff --git a/GUIWidgets/MRML/vtkMRMLGUIWidgetNode.cxx b/GUIWidgets/MRML/vtkMRMLGUIWidgetNode.cxx new file mode 100644 index 0000000..e9b8c5a --- /dev/null +++ b/GUIWidgets/MRML/vtkMRMLGUIWidgetNode.cxx @@ -0,0 +1,140 @@ +/*============================================================================== + + Program: 3D Slicer + + Portions (c) Copyright Brigham and Women's Hospital (BWH) All Rights Reserved. + + See COPYRIGHT.txt + or http://www.slicer.org/copyright/copyright.txt for details. + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + This file was originally developed by Csaba Pinter, EBATINCA, S.L., and + development was supported by "ICEX Espana Exportacion e Inversiones" under + the program "Inversiones de Empresas Extranjeras en Actividades de I+D + (Fondo Tecnologico)- Convocatoria 2021" + +==============================================================================*/ + +// GUI Widgets includes +#include "vtkMRMLGUIWidgetNode.h" +#include "vtkMRMLGUIWidgetDisplayNode.h" + +// MRML includes +#include "vtkMRMLMeasurementArea.h" +#include "vtkMRMLScene.h" +#include "vtkMRMLStorageNode.h" +#include "vtkMRMLTransformNode.h" + +// VTK includes +#include +#include +#include +#include +#include +#include +#include + +// STD includes +#include + +//---------------------------------------------------------------------------- +vtkMRMLNodeNewMacro(vtkMRMLGUIWidgetNode); + +//---------------------------------------------------------------------------- +vtkMRMLGUIWidgetNode::vtkMRMLGUIWidgetNode() +{ +} + +//---------------------------------------------------------------------------- +vtkMRMLGUIWidgetNode::~vtkMRMLGUIWidgetNode() = default; + +//---------------------------------------------------------------------------- +void vtkMRMLGUIWidgetNode::WriteXML(ostream& of, int nIndent) +{ + Superclass::WriteXML(of,nIndent); + //vtkMRMLWriteXMLBeginMacro(of); + //vtkMRMLWriteXMLEnumMacro(sizeMode, SizeMode); + //vtkMRMLWriteXMLVectorMacro(size, Size, double, 2); + //vtkMRMLWriteXMLFloatMacro(autoSizeScalingFactor, AutoSizeScalingFactor); + //vtkMRMLWriteXMLMatrix4x4Macro(objectToBaseMatrix, ObjectToBaseMatrix); + //vtkMRMLWriteXMLEndMacro(); +} + +//---------------------------------------------------------------------------- +void vtkMRMLGUIWidgetNode::ReadXMLAttributes(const char** atts) +{ + MRMLNodeModifyBlocker blocker(this); + Superclass::ReadXMLAttributes(atts); + //vtkMRMLReadXMLBeginMacro(atts); + //vtkMRMLReadXMLEnumMacro(sizeMode, SizeMode); + //vtkMRMLReadXMLVectorMacro(size, Size, double, 2); + //vtkMRMLReadXMLFloatMacro(autoSizeScalingFactor, AutoSizeScalingFactor); + //vtkMRMLReadXMLOwnedMatrix4x4Macro(planeTobaseMatrix, ObjectToBaseMatrix); // Backwards compatible with old name + //vtkMRMLReadXMLOwnedMatrix4x4Macro(objectToBaseMatrix, ObjectToBaseMatrix); + //vtkMRMLReadXMLEndMacro(); +} + +//---------------------------------------------------------------------------- +void vtkMRMLGUIWidgetNode::CopyContent(vtkMRMLNode* anode, bool deepCopy/*=true*/) +{ + MRMLNodeModifyBlocker blocker(this); + Superclass::CopyContent(anode, deepCopy); + //vtkMRMLCopyBeginMacro(anode); + //vtkMRMLCopyEnumMacro(SizeMode); + //vtkMRMLCopyVectorMacro(Size, double, 2); + //vtkMRMLCopyFloatMacro(AutoSizeScalingFactor); + //vtkMRMLCopyOwnedMatrix4x4Macro(ObjectToBaseMatrix); + //vtkMRMLCopyEndMacro(); +} + +//---------------------------------------------------------------------------- +void vtkMRMLGUIWidgetNode::PrintSelf(ostream& os, vtkIndent indent) +{ + Superclass::PrintSelf(os,indent); + //vtkMRMLPrintBeginMacro(os, indent); + //vtkMRMLPrintEnumMacro(SizeMode); + //vtkMRMLPrintVectorMacro(Size, double, 2); + //vtkMRMLPrintFloatMacro(AutoSizeScalingFactor); + //vtkMRMLPrintMatrix4x4Macro(ObjectToBaseMatrix); + //vtkMRMLPrintEndMacro(); +} + +//---------------------------------------------------------------------------- +void vtkMRMLGUIWidgetNode::CreateDefaultDisplayNodes() +{ + if (this->GetDisplayNode() != nullptr && vtkMRMLGUIWidgetDisplayNode::SafeDownCast(this->GetDisplayNode()) != nullptr) + { + // Display node already exists + return; + } + if (this->GetScene() == nullptr) + { + vtkErrorMacro("vtkMRMLGUIWidgetNode::CreateDefaultDisplayNodes failed: scene is invalid"); + return; + } + vtkMRMLGUIWidgetDisplayNode* displayNode = vtkMRMLGUIWidgetDisplayNode::SafeDownCast( + this->GetScene()->AddNewNodeByClass("vtkMRMLGUIWidgetDisplayNode")); + if (!displayNode) + { + vtkErrorMacro("vtkMRMLGUIWidgetNode::CreateDefaultDisplayNodes failed: scene failed to instantiate a vtkMRMLGUIWidgetDisplayNode node"); + return; + } + this->SetAndObserveDisplayNodeID(displayNode->GetID()); +} + +//---------------------------------------------------------------------------- +void vtkMRMLGUIWidgetNode::SetWidget(void* w) +{ + if (this->Widget == w) + { + return; + } + + this->Widget = w; + this->Modified(); +} diff --git a/GUIWidgets/MRML/vtkMRMLGUIWidgetNode.h b/GUIWidgets/MRML/vtkMRMLGUIWidgetNode.h new file mode 100644 index 0000000..a13ec58 --- /dev/null +++ b/GUIWidgets/MRML/vtkMRMLGUIWidgetNode.h @@ -0,0 +1,85 @@ +/*============================================================================== + + Program: 3D Slicer + + Portions (c) Copyright Brigham and Women's Hospital (BWH) All Rights Reserved. + + See COPYRIGHT.txt + or http://www.slicer.org/copyright/copyright.txt for details. + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + This file was originally developed by Csaba Pinter, EBATINCA, S.L., and + development was supported by "ICEX Espana Exportacion e Inversiones" under + the program "Inversiones de Empresas Extranjeras en Actividades de I+D + (Fondo Tecnologico)- Convocatoria 2021" + +==============================================================================*/ + +#ifndef __vtkMRMLGUIWidgetNode_h +#define __vtkMRMLGUIWidgetNode_h + +#include "vtkSlicerGUIWidgetsModuleMRMLExport.h" + +// Markups includes +#include "vtkMRMLMarkupsPlaneNode.h" + +/// \brief MRML node to represent a planar GUI widget displayable in the 3D view +class VTK_SLICER_GUIWIDGETS_MODULE_MRML_EXPORT vtkMRMLGUIWidgetNode : public vtkMRMLMarkupsPlaneNode +{ +public: + static vtkMRMLGUIWidgetNode *New(); + vtkTypeMacro(vtkMRMLGUIWidgetNode, vtkMRMLMarkupsPlaneNode); + /// Print out the node information to the output stream + void PrintSelf(ostream& os, vtkIndent indent) override; + + //TODO: + //const char* GetIcon() override {return ":/Icons/MarkupsPlane.png";} + //const char* GetAddIcon() override {return ":/Icons/MarkupsPlaneMouseModePlace.png";} + //const char* GetPlaceAddIcon() override {return ":/Icons/MarkupsPlaneMouseModePlaceAdd.png";} + + vtkMRMLNode* CreateNodeInstance() override; + /// Get node XML tag name (like Volume, Model) + const char* GetNodeTagName() override {return "GUIWidget";} + + /// Get markup name + const char* GetMarkupType() override {return "GUIWidget";}; + + /// Get markup short name + const char* GetDefaultNodeNamePrefix() override {return "W";}; + + /// Read node attributes from XML file + void ReadXMLAttributes( const char** atts) override; + + /// Write this node's information to a MRML file in XML format. + void WriteXML(ostream& of, int indent) override; + + /// Copy node content (excludes basic data, such as name and node references). + /// \sa vtkMRMLNode::CopyContent + vtkMRMLCopyContentMacro(vtkMRMLGUIWidgetNode); + + /// Create default storage node or nullptr if does not have one + void CreateDefaultDisplayNodes() override; + +public: + /// Set the handle (must contain a QWidget) that will receive the events. + void SetWidget(void* w); + /// Get the QWidget handle + void* GetWidget() { return this->Widget; }; + +protected: + /// Handle for the widget to be rendered in the GUI widget markup + void* Widget{nullptr}; + +protected: + vtkMRMLGUIWidgetNode(); + ~vtkMRMLGUIWidgetNode() override; + vtkMRMLGUIWidgetNode(const vtkMRMLGUIWidgetNode&); + void operator=(const vtkMRMLGUIWidgetNode&); +}; + +#endif diff --git a/GUIWidgets/MRMLDM/CMakeLists.txt b/GUIWidgets/MRMLDM/CMakeLists.txt new file mode 100644 index 0000000..c476032 --- /dev/null +++ b/GUIWidgets/MRMLDM/CMakeLists.txt @@ -0,0 +1,47 @@ +project(vtkSlicer${MODULE_NAME}ModuleMRMLDisplayableManager) + +set(KIT ${PROJECT_NAME}) + +set(${KIT}_EXPORT_DIRECTIVE "VTK_SLICER_${MODULE_NAME_UPPER}_MODULE_MRMLDISPLAYABLEMANAGER_EXPORT") + +set(${KIT}_INCLUDE_DIRECTORIES + ${vtkSlicer${MODULE_NAME}ModuleLogic_SOURCE_DIR} + ${vtkSlicer${MODULE_NAME}ModuleLogic_BINARY_DIR} + ${vtkSlicer${MODULE_NAME}ModuleMRML_SOURCE_DIR} + ${vtkSlicer${MODULE_NAME}ModuleMRML_BINARY_DIR} + ${vtkSlicer${MODULE_NAME}ModuleVTKWidgets_SOURCE_DIR} + ${vtkSlicer${MODULE_NAME}ModuleVTKWidgets_BINARY_DIR} +) + +set(DISPLAYABLE_MANAGER_SRCS + vtkMRML${MODULE_NAME}DisplayableManager.cxx + ) + +SlicerConfigureDisplayableManagerObjectFactory( + TARGET_NAME ${KIT} + SRCS "${DISPLAYABLE_MANAGER_SRCS}" + EXPORT_MACRO "${${KIT}_EXPORT_DIRECTIVE}" + EXPORT_HEADER "${KIT}Export.h" + OUTPUT_SRCS_VAR DISPLAYABLE_MANAGER_INSTANTIATOR_SRCS + ) + +set(${KIT}_SRCS + ${DISPLAYABLE_MANAGER_INSTANTIATOR_SRCS} + ${DISPLAYABLE_MANAGER_SRCS} + ) + +set(${KIT}_TARGET_LIBRARIES + ${MRML_LIBRARIES} + vtkSlicer${MODULE_NAME}ModuleLogic + vtkSlicer${MODULE_NAME}ModuleMRML + vtkSlicer${MODULE_NAME}ModuleVTKWidgets +) + +#----------------------------------------------------------------------------- +SlicerMacroBuildModuleLogic( + NAME ${KIT} + EXPORT_DIRECTIVE ${${KIT}_EXPORT_DIRECTIVE} + INCLUDE_DIRECTORIES ${${KIT}_INCLUDE_DIRECTORIES} + SRCS ${${KIT}_SRCS} + TARGET_LIBRARIES ${${KIT}_TARGET_LIBRARIES} +) diff --git a/GUIWidgets/MRMLDM/vtkMRMLGUIWidgetsDisplayableManager.cxx b/GUIWidgets/MRMLDM/vtkMRMLGUIWidgetsDisplayableManager.cxx new file mode 100644 index 0000000..7ed84f7 --- /dev/null +++ b/GUIWidgets/MRMLDM/vtkMRMLGUIWidgetsDisplayableManager.cxx @@ -0,0 +1,1319 @@ +/*============================================================================== + + Program: 3D Slicer + + Portions (c) Copyright Brigham and Women's Hospital (BWH) All Rights Reserved. + + See COPYRIGHT.txt + or http://www.slicer.org/copyright/copyright.txt for details. + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + This file was originally developed by Csaba Pinter, EBATINCA, S.L., and + development was supported by "ICEX Espana Exportacion e Inversiones" under + the program "Inversiones de Empresas Extranjeras en Actividades de I+D + (Fondo Tecnologico)- Convocatoria 2021" + +==============================================================================*/ + +#include "vtkMRMLGUIWidgetsDisplayableManager.h" + +#include "vtkMRMLAbstractSliceViewDisplayableManager.h" + +// GUI widgets includes +#include +#include + +// MarkupsModule/Logic includes +//#include + +// MRMLDisplayableManager includes +#include +#include + +// MRML includes +#include +#include +#include +#include +#include +//#include +//#include +//#include +#include +#include + +// VTK includes +#include +#include +//#include +#include +//#include +//#include +//#include +#include +#include +#include +//#include +//#include +//#include +#include +#include +//#include +//#include +//#include +//#include +//#include +//#include + +// STD includes +//#include +//#include +//#include +//#include +//#include + +//typedef void (*fp)(); + +//#define NUMERIC_ZERO 0.001 + +//--------------------------------------------------------------------------- +vtkStandardNewMacro(vtkMRMLGUIWidgetsDisplayableManager); + +//--------------------------------------------------------------------------- +class vtkMRMLGUIWidgetsDisplayableManager::vtkInternal +{ +public: + vtkInternal(vtkMRMLGUIWidgetsDisplayableManager* external); + ~vtkInternal(); + + //struct Pipeline + // { + + // Pipeline() + // { + // this->Actor = vtkSmartPointer::New(); + // vtkNew mapper; + // mapper->SetScalarVisibility(false); // ignore any scalars that an input mesh may contain + // this->Actor->SetMapper(mapper.GetPointer()); + // this->Actor->SetVisibility(false); + + // this->NodeToWorldTransform = vtkSmartPointer::New(); + // this->ModelWarper = vtkSmartPointer::New(); + // this->ModelWarper->SetTransform(this->NodeToWorldTransform); + // mapper->SetInputConnection(this->ModelWarper->GetOutputPort()); + // } + + // vtkSmartPointer Actor; + // vtkSmartPointer NodeToWorldTransform; + // vtkSmartPointer ModelWarper; + // }; + + //typedef std::map PipelineMapType; // first: segment ID; second: display pipeline + //typedef std::map PipelinesCacheType; + //PipelinesCacheType DisplayPipelines; + + //typedef std::map < vtkMRMLSegmentationNode*, std::set< vtkMRMLSegmentationDisplayNode* > > SegmentationToDisplayCacheType; + //SegmentationToDisplayCacheType SegmentationToDisplayNodes; + + /// Get widget for a given display node + vtkSlicerQWidgetWidget* GetWidget(vtkMRMLGUIWidgetDisplayNode* guiWidgetDisplayNode); + /// Get first visible widget for this markup + vtkSlicerQWidgetWidget* GetWidget(vtkMRMLGUIWidgetNode* guiWidgetNode); + + /// Remove all widgets, intersection widgets, nodes + void RemoveAllWidgetsAndNodes(); + + void AddGUIWidgetNode(vtkMRMLGUIWidgetNode* node); + void RemoveGUIWidgetNode(vtkMRMLGUIWidgetNode* node); + void AddDisplayNode(vtkMRMLGUIWidgetDisplayNode* displayNode); + void RemoveDisplayNode(vtkMRMLGUIWidgetDisplayNode* displayNode); + + void DeleteWidget(vtkSlicerQWidgetWidget* widget); + + void AddObservations(vtkMRMLGUIWidgetNode* node); + void RemoveObservations(vtkMRMLGUIWidgetNode* node); + +public: + /// Picker of segment prop in renderer + //vtkSmartPointer CellPicker; + + /// Last picked segmentation display node ID + //std::string PickedDisplayNodeID; + + /// Map of vtkWidget indexed using associated node ID + typedef std::map, vtkSlicerQWidgetWidget* > DisplayNodeToWidgetType; + typedef std::map, vtkSlicerQWidgetWidget* >::iterator DisplayNodeToWidgetIt; + DisplayNodeToWidgetType GUIWidgetDisplayNodesToWidgets; // display nodes with widgets assigned + + typedef std::set > GUIWidgetNodesType; + typedef std::set >::iterator GUIWidgetNodesIt; + GUIWidgetNodesType GUIWidgetNodes; // observed GUI widget nodes + +private: + vtkMRMLGUIWidgetsDisplayableManager* External; + + bool AddingGUIWidgetNode; + + std::vector ObservedGuiWidgetNodeEvents; +}; + +//--------------------------------------------------------------------------- +// vtkInternal methods + +//--------------------------------------------------------------------------- +vtkMRMLGUIWidgetsDisplayableManager::vtkInternal::vtkInternal(vtkMRMLGUIWidgetsDisplayableManager* external) +: External(external) +, AddingGUIWidgetNode(false) +//, AddingSegmentationNode(false) +{ + //this->CellPicker = vtkSmartPointer::New(); + //this->CellPicker->SetTolerance(0.00001); +} + +//--------------------------------------------------------------------------- +vtkMRMLGUIWidgetsDisplayableManager::vtkInternal::~vtkInternal() +{ +// this->ClearDisplayableNodes(); +} + +//--------------------------------------------------------------------------- +vtkSlicerQWidgetWidget* vtkMRMLGUIWidgetsDisplayableManager::vtkInternal::GetWidget(vtkMRMLGUIWidgetNode* guiWidgetNode) +{ + if (!guiWidgetNode) + { + return nullptr; + } + vtkMRMLGUIWidgetsDisplayableManager::vtkInternal::GUIWidgetNodesIt displayableIt = this->GUIWidgetNodes.find(guiWidgetNode); + if (displayableIt == this->GUIWidgetNodes.end()) + { + // we do not manage this markup + return nullptr; + } + + // Return first widget found for a GUI widget node + for (vtkMRMLGUIWidgetsDisplayableManager::vtkInternal::DisplayNodeToWidgetIt widgetIterator = this->GUIWidgetDisplayNodesToWidgets.begin(); + widgetIterator != this->GUIWidgetDisplayNodesToWidgets.end(); ++widgetIterator) + { + vtkMRMLGUIWidgetDisplayNode* guiWidgetDisplayNode = widgetIterator->first; + if (guiWidgetDisplayNode->GetDisplayableNode() == guiWidgetNode) + { + return widgetIterator->second; + } + } + + return nullptr; +} + +//--------------------------------------------------------------------------- +vtkSlicerQWidgetWidget* vtkMRMLGUIWidgetsDisplayableManager::vtkInternal::GetWidget(vtkMRMLGUIWidgetDisplayNode* node) +{ + if (!node) + { + return nullptr; + } + + // Make sure the map contains a vtkWidget associated with this node + DisplayNodeToWidgetIt it = this->GUIWidgetDisplayNodesToWidgets.find(node); + if (it == this->GUIWidgetDisplayNodesToWidgets.end()) + { + return nullptr; + } + + return it->second; +} + +//--------------------------------------------------------------------------- +void vtkMRMLGUIWidgetsDisplayableManager::vtkInternal::AddGUIWidgetNode(vtkMRMLGUIWidgetNode* node) +{ + if (!node) + { + return; + } + vtkMRMLAbstractViewNode* viewNode = vtkMRMLAbstractViewNode::SafeDownCast(this->External->GetMRMLDisplayableNode()); + if (!viewNode) + { + return; + } + + if (this->AddingGUIWidgetNode) + { + return; + } + this->AddingGUIWidgetNode = true; + + this->AddObservations(node); + this->GUIWidgetNodes.insert(node); + + // Add Display Nodes + int nnodes = node->GetNumberOfDisplayNodes(); + for (int i = 0; iGetNthDisplayNode(i)); + + // Check whether DisplayNode should be shown in this view + if (!displayNode + || !displayNode->IsA("vtkMRMLGUIWidgetDisplayNode") + || !displayNode->IsDisplayableInView(viewNode->GetID())) + { + continue; + } + + this->AddDisplayNode(displayNode); + } + + this->AddingGUIWidgetNode = false; +} + +//--------------------------------------------------------------------------- +void vtkMRMLGUIWidgetsDisplayableManager::vtkInternal::RemoveGUIWidgetNode(vtkMRMLGUIWidgetNode* node) +{ + if (!node) + { + return; + } + + vtkMRMLGUIWidgetsDisplayableManager::vtkInternal::GUIWidgetNodesIt displayableIt = + this->GUIWidgetNodes.find(node); + + if (displayableIt == this->GUIWidgetNodes.end()) + { + // we do not manage this markup + return; + } + + // Remove display nodes corresponding to this GUI widget node + for (vtkMRMLGUIWidgetsDisplayableManager::vtkInternal::DisplayNodeToWidgetIt widgetIterator = this->GUIWidgetDisplayNodesToWidgets.begin(); + widgetIterator != this->GUIWidgetDisplayNodesToWidgets.end(); + /*upon deletion the increment is done already, so don't increment here*/) + { + vtkMRMLGUIWidgetDisplayNode* guiWidgetDisplayNode = widgetIterator->first; + if (guiWidgetDisplayNode->GetDisplayableNode() != node) + { + ++widgetIterator; + } + else + { + // display node of the node that is being removed + vtkMRMLGUIWidgetsDisplayableManager::vtkInternal::DisplayNodeToWidgetIt widgetIteratorToRemove = widgetIterator; + ++widgetIterator; + vtkSlicerQWidgetWidget* widgetToRemove = widgetIteratorToRemove->second; + this->DeleteWidget(widgetToRemove); + this->GUIWidgetDisplayNodesToWidgets.erase(widgetIteratorToRemove); + } + } + + this->RemoveObservations(node); + this->GUIWidgetNodes.erase(displayableIt); +} + +//--------------------------------------------------------------------------- +void vtkMRMLGUIWidgetsDisplayableManager::vtkInternal::AddDisplayNode(vtkMRMLGUIWidgetDisplayNode* guiWidgetDisplayNode) +{ + if (!guiWidgetDisplayNode) + { + return; + } + + // Do not add the display node if displayNodeIt is already associated with a widget object. + // This happens when a segmentation node already associated with a display node + // is copied into an other (using vtkMRMLNode::Copy()) and is added to the scene afterward. + // Related issue are #3428 and #2608 + vtkMRMLGUIWidgetsDisplayableManager::vtkInternal::DisplayNodeToWidgetIt displayNodeIt + = this->GUIWidgetDisplayNodesToWidgets.find(guiWidgetDisplayNode); + if (displayNodeIt != this->GUIWidgetDisplayNodesToWidgets.end()) + { + return; + } + + // There should not be a widget for the new node + if (this->GetWidget(guiWidgetDisplayNode) != nullptr) + { + vtkErrorWithObjectMacro(this->External, "vtkMRMLGUIWidgetsDisplayableManager: A widget is already associated to this node"); + return; + } + + vtkSlicerQWidgetWidget* newWidget = this->External->CreateWidget(guiWidgetDisplayNode); + if (!newWidget) + { + vtkErrorWithObjectMacro(this->External, "vtkMRMLGUIWidgetsDisplayableManager: Failed to create widget"); + return; + } + + // record the mapping between node and widget in the helper + this->GUIWidgetDisplayNodesToWidgets[guiWidgetDisplayNode] = newWidget; + + // Build representation + newWidget->UpdateFromMRML(guiWidgetDisplayNode, 0); // no specific event triggers full rebuild + + this->External->RequestRender(); + + // Update cached matrices. Calls UpdateWidget + //this->UpdateDisplayableTransforms(mNode); +} + +//--------------------------------------------------------------------------- +void vtkMRMLGUIWidgetsDisplayableManager::vtkInternal::RemoveDisplayNode(vtkMRMLGUIWidgetDisplayNode* guiWidgetDisplayNode) +{ + if (!guiWidgetDisplayNode) + { + return; + } + + vtkMRMLGUIWidgetsDisplayableManager::vtkInternal::DisplayNodeToWidgetIt displayNodeIt + = this->GUIWidgetDisplayNodesToWidgets.find(guiWidgetDisplayNode); + if (displayNodeIt == this->GUIWidgetDisplayNodesToWidgets.end()) + { + // no widget found for this display node + return; + } + + vtkSlicerQWidgetWidget* widget = (displayNodeIt->second); + this->DeleteWidget(widget); + + this->GUIWidgetDisplayNodesToWidgets.erase(guiWidgetDisplayNode); +} + +//--------------------------------------------------------------------------- +void vtkMRMLGUIWidgetsDisplayableManager::vtkInternal::RemoveAllWidgetsAndNodes() +{ + DisplayNodeToWidgetIt guiWidgetIterator = this->GUIWidgetDisplayNodesToWidgets.begin(); + for (guiWidgetIterator = this->GUIWidgetDisplayNodesToWidgets.begin(); + guiWidgetIterator != this->GUIWidgetDisplayNodesToWidgets.end(); ++guiWidgetIterator) + { + guiWidgetIterator->second->Delete(); + } + this->GUIWidgetDisplayNodesToWidgets.clear(); + + GUIWidgetNodesIt guiWidgetNodeIterator = this->GUIWidgetNodes.begin(); + for (guiWidgetNodeIterator = this->GUIWidgetNodes.begin(); + guiWidgetNodeIterator != this->GUIWidgetNodes.end(); ++guiWidgetNodeIterator) + { + this->RemoveObservations(*guiWidgetNodeIterator); + } +} + +//--------------------------------------------------------------------------- +void vtkMRMLGUIWidgetsDisplayableManager::vtkInternal::DeleteWidget(vtkSlicerQWidgetWidget* widget) +{ + if (!widget) + { + return; + } + widget->SetRenderer(nullptr); + widget->SetRepresentation(nullptr); + widget->Delete(); +} + +//--------------------------------------------------------------------------- +void vtkMRMLGUIWidgetsDisplayableManager::vtkInternal::AddObservations(vtkMRMLGUIWidgetNode* node) +{ + vtkCallbackCommand* callbackCommand = this->External->GetMRMLNodesCallbackCommand(); + vtkEventBroker* broker = vtkEventBroker::GetInstance(); + for (auto observedMarkupNodeEvent : this->ObservedGuiWidgetNodeEvents) + { + if (!broker->GetObservationExist(node, observedMarkupNodeEvent, this->External, callbackCommand)) + { + broker->AddObservation(node, observedMarkupNodeEvent, this->External, callbackCommand); + } + } +} + +//--------------------------------------------------------------------------- +void vtkMRMLGUIWidgetsDisplayableManager::vtkInternal::RemoveObservations(vtkMRMLGUIWidgetNode* node) +{ + vtkCallbackCommand* callbackCommand = this->External->GetMRMLNodesCallbackCommand(); + vtkEventBroker* broker = vtkEventBroker::GetInstance(); + for (auto observedMarkupNodeEvent : this->ObservedGuiWidgetNodeEvents) + { + vtkEventBroker::ObservationVector observations; + observations = broker->GetObservations(node, observedMarkupNodeEvent, this->External, callbackCommand); + broker->RemoveObservations(observations); + } +} + + +//--------------------------------------------------------------------------- +// vtkMRMLGUIWidgetsDisplayableManager methods + +//--------------------------------------------------------------------------- +vtkMRMLGUIWidgetsDisplayableManager::vtkMRMLGUIWidgetsDisplayableManager() +{ + this->Internal = new vtkInternal(this); + + /* + this->Internal = vtkSmartPointer::New(); + this->Internal->SetDisplayableManager(this); + this->DisableInteractorStyleEventsProcessing = 0; + + this->LastClickWorldCoordinates[0]=0.0; + this->LastClickWorldCoordinates[1]=0.0; + this->LastClickWorldCoordinates[2]=0.0; + this->LastClickWorldCoordinates[3]=1.0; + */ +} + +//--------------------------------------------------------------------------- +vtkMRMLGUIWidgetsDisplayableManager::~vtkMRMLGUIWidgetsDisplayableManager() +{ + //this->DisableInteractorStyleEventsProcessing = 0; + + delete this->Internal; + this->Internal = nullptr; +} + +//--------------------------------------------------------------------------- +void vtkMRMLGUIWidgetsDisplayableManager::PrintSelf(ostream& os, vtkIndent indent) +{ + this->Superclass::PrintSelf(os, indent); + /* + os << indent << "DisableInteractorStyleEventsProcessing = " << this->DisableInteractorStyleEventsProcessing << std::endl; + if (this->SliceNode && + this->SliceNode->GetID()) + { + os << indent << "Slice node id = " << this->SliceNode->GetID() << std::endl; + } + else + { + os << indent << "No slice node" << std::endl; + } + */ +} +/* +//--------------------------------------------------------------------------- +vtkMRMLSliceNode * vtkMRMLGUIWidgetsDisplayableManager::GetMRMLSliceNode() +{ + return vtkMRMLSliceNode::SafeDownCast(this->GetMRMLDisplayableNode()); +} + +//--------------------------------------------------------------------------- +bool vtkMRMLGUIWidgetsDisplayableManager::Is2DDisplayableManager() +{ + return this->GetMRMLSliceNode() != nullptr; +} +*/ +//--------------------------------------------------------------------------- +void vtkMRMLGUIWidgetsDisplayableManager::RequestRender() +{ + if (!this->GetMRMLScene()) + { + return; + } + if (!this->GetMRMLScene()->IsBatchProcessing()) + { + this->Superclass::RequestRender(); + } +} + +//--------------------------------------------------------------------------- +void vtkMRMLGUIWidgetsDisplayableManager::UpdateFromMRML() +{ + // this gets called from RequestRender, so make sure to jump out quickly if possible + if (this->GetMRMLScene() == nullptr) + { + return; + } + + // turn off update from mrml requested, as we're doing it now, and create + // widget requests a render which checks this flag before calling update + // from mrml again + this->SetUpdateFromMRMLRequested(false); + + std::vector guiWidgetNodes; + this->GetMRMLScene()->GetNodesByClass("vtkMRMLGUIWidgetNode", guiWidgetNodes); + for (std::vector< vtkMRMLNode* >::iterator nodeIt = guiWidgetNodes.begin(); nodeIt != guiWidgetNodes.end(); ++nodeIt) + { + vtkMRMLGUIWidgetNode* guiWidgetNode = vtkMRMLGUIWidgetNode::SafeDownCast(*nodeIt); + if (!guiWidgetNode) + { + continue; + } + if (this->Internal->GUIWidgetNodes.find(guiWidgetNode) != this->Internal->GUIWidgetNodes.end()) + { + // node added already + continue; + } + this->Internal->AddGUIWidgetNode(guiWidgetNode); + } + + std::vector guiWidgetDisplayNodes; + this->GetMRMLScene()->GetNodesByClass("vtkMRMLGUIWidgetDisplayNode", guiWidgetDisplayNodes); + for (std::vector< vtkMRMLNode* >::iterator nodeIt = guiWidgetDisplayNodes.begin(); + nodeIt != guiWidgetDisplayNodes.end(); ++nodeIt) + { + vtkMRMLGUIWidgetDisplayNode* guiWidgetDisplayNode = vtkMRMLGUIWidgetDisplayNode::SafeDownCast(*nodeIt); + if (!guiWidgetDisplayNode) + { + continue; + } + if (this->Internal->GUIWidgetDisplayNodesToWidgets.find(guiWidgetDisplayNode) != this->Internal->GUIWidgetDisplayNodesToWidgets.end()) + { + // node added already + continue; + } + this->Internal->AddDisplayNode(guiWidgetDisplayNode); + } + + // Remove observed GUI widget nodes that have been deleted from the scene + for (vtkMRMLGUIWidgetsDisplayableManager::vtkInternal::GUIWidgetNodesIt guiWidgetIterator = + this->Internal->GUIWidgetNodes.begin(); guiWidgetIterator != this->Internal->GUIWidgetNodes.end(); ) + { + vtkMRMLGUIWidgetNode* guiWidgetNode = *guiWidgetIterator; + if (this->GetMRMLScene()->IsNodePresent(guiWidgetNode)) + { + ++guiWidgetIterator; + } + else + { + // display node is not in the scene anymore, delete the widget + vtkMRMLGUIWidgetsDisplayableManager::vtkInternal::GUIWidgetNodesIt guiWidgetIteratorToRemove = guiWidgetIterator; + ++guiWidgetIterator; + this->Internal->RemoveGUIWidgetNode(*guiWidgetIteratorToRemove); + this->Internal->GUIWidgetNodes.erase(guiWidgetIteratorToRemove); + } + } + + // Remove widgets corresponding deleted display nodes + for (vtkMRMLGUIWidgetsDisplayableManager::vtkInternal::DisplayNodeToWidgetIt widgetIterator = this->Internal->GUIWidgetDisplayNodesToWidgets.begin(); + widgetIterator != this->Internal->GUIWidgetDisplayNodesToWidgets.end(); ) + { + vtkMRMLGUIWidgetDisplayNode* guiWidgetDisplayNode = widgetIterator->first; + if (this->GetMRMLScene()->IsNodePresent(guiWidgetDisplayNode)) + { + ++widgetIterator; + } + else + { + // display node is not in the scene anymore, delete the widget + vtkMRMLGUIWidgetsDisplayableManager::vtkInternal::DisplayNodeToWidgetIt widgetIteratorToRemove = widgetIterator; + ++widgetIterator; + vtkSlicerQWidgetWidget* widgetToRemove = widgetIteratorToRemove->second; + this->Internal->DeleteWidget(widgetToRemove); + this->Internal->GUIWidgetDisplayNodesToWidgets.erase(widgetIteratorToRemove); + } + } +} + +//--------------------------------------------------------------------------- +void vtkMRMLGUIWidgetsDisplayableManager::SetMRMLSceneInternal(vtkMRMLScene* newScene) +{ + Superclass::SetMRMLSceneInternal(newScene); + + // after a new scene got associated, we want to make sure everything old is gone + this->OnMRMLSceneEndClose(); + + //if (newScene) + //{ + // this->AddObserversToInteractionNode(); + //} + //else + //{ + // // there's no scene to get the interaction node from, so this won't do anything + // this->RemoveObserversFromInteractionNode(); + //} + //vtkDebugMacro("SetMRMLSceneInternal: add observer on interaction node now?"); + + // clear out the map of glyph types + //this->Internal->ClearNodeGlyphTypes(); +} + +//--------------------------------------------------------------------------- +void vtkMRMLGUIWidgetsDisplayableManager +::ProcessMRMLNodesEvents(vtkObject *caller, unsigned long event, void *callData) +{ + vtkMRMLGUIWidgetNode* guiWidgetNode = vtkMRMLGUIWidgetNode::SafeDownCast(caller); + vtkMRMLInteractionNode* interactionNode = vtkMRMLInteractionNode::SafeDownCast(caller); + if (guiWidgetNode) + { + bool renderRequested = false; + + for (int displayNodeIndex = 0; displayNodeIndex < guiWidgetNode->GetNumberOfDisplayNodes(); displayNodeIndex++) + { + vtkMRMLGUIWidgetDisplayNode* displayNode = vtkMRMLGUIWidgetDisplayNode::SafeDownCast(guiWidgetNode->GetNthDisplayNode(displayNodeIndex)); + vtkSlicerQWidgetWidget* widget = this->Internal->GetWidget(displayNode); + if (!widget) + { + // if a new display node is added or display node view node IDs are changed then we may need to create a new widget + this->Internal->AddDisplayNode(displayNode); + widget = this->Internal->GetWidget(displayNode); + } + if (!widget) + { + continue; + } + widget->UpdateFromMRML(guiWidgetNode, event, callData); + if (widget->GetNeedToRender()) + { + renderRequested = true; + widget->NeedToRenderOff(); + } + } + + if (renderRequested) + { + this->RequestRender(); + } + } + else if (interactionNode) + { + if (event == vtkMRMLInteractionNode::InteractionModeChangedEvent) + { + // loop through all widgets and update the widget status + for (vtkMRMLGUIWidgetsDisplayableManager::vtkInternal::DisplayNodeToWidgetIt widgetIterator = this->Internal->GUIWidgetDisplayNodesToWidgets.begin(); + widgetIterator != this->Internal->GUIWidgetDisplayNodesToWidgets.end(); ++widgetIterator) + { + vtkSlicerQWidgetWidget* widget = widgetIterator->second; + if (!widget) + { + continue; + } + vtkMRMLInteractionEventData* eventData = reinterpret_cast(callData); + widget->Leave(eventData); + } + } + } + else + { + this->Superclass::ProcessMRMLNodesEvents(caller, event, callData); + } +} + +//--------------------------------------------------------------------------- +void vtkMRMLGUIWidgetsDisplayableManager::OnMRMLSceneEndClose() +{ + //vtkDebugMacro("OnMRMLSceneEndClose: remove observers?"); + // run through all nodes and remove node and widget + this->Internal->RemoveAllWidgetsAndNodes(); + + this->SetUpdateFromMRMLRequested(true); + this->RequestRender(); +} + +//--------------------------------------------------------------------------- +void vtkMRMLGUIWidgetsDisplayableManager::OnMRMLSceneEndImport() +{ + this->SetUpdateFromMRMLRequested(true); + this->UpdateFromMRMLScene(); + //this->Internal->SetAllWidgetsToManipulate(); + this->RequestRender(); +} + +//--------------------------------------------------------------------------- +void vtkMRMLGUIWidgetsDisplayableManager::UpdateFromMRMLScene() +{ + if (this->GetMRMLDisplayableNode()) + { + this->UpdateFromMRML(); + } +} + +//--------------------------------------------------------------------------- +void vtkMRMLGUIWidgetsDisplayableManager::OnMRMLSceneNodeAdded(vtkMRMLNode* node) +{ + if (!node || !this->GetMRMLScene()) + { + return; + } + + // if the scene is still updating, jump out + if (this->GetMRMLScene()->IsBatchProcessing()) + { + this->SetUpdateFromMRMLRequested(true); + return; + } + + if (node->IsA("vtkMRMLInteractionNode")) + { + //this->AddObserversToInteractionNode(); + return; + } + + if (node->IsA("vtkMRMLGUIWidgetNode")) + { + this->Internal->AddGUIWidgetNode(vtkMRMLGUIWidgetNode::SafeDownCast(node)); + + // and render again + this->RequestRender(); + } +} +/* +//--------------------------------------------------------------------------- +void vtkMRMLGUIWidgetsDisplayableManager::AddObserversToInteractionNode() +{ + if (!this->GetMRMLScene()) + { + return; + } + // also observe the interaction node for changes + vtkMRMLInteractionNode *interactionNode = this->GetInteractionNode(); + if (interactionNode) + { + vtkDebugMacro("AddObserversToInteractionNode: interactionNode found"); + vtkNew interactionEvents; + interactionEvents->InsertNextValue(vtkMRMLInteractionNode::InteractionModeChangedEvent); + interactionEvents->InsertNextValue(vtkMRMLInteractionNode::InteractionModePersistenceChangedEvent); + interactionEvents->InsertNextValue(vtkMRMLInteractionNode::EndPlacementEvent); + vtkObserveMRMLNodeEventsMacro(interactionNode, interactionEvents.GetPointer()); + } + else { vtkDebugMacro("AddObserversToInteractionNode: No interaction node!"); } +} + +//--------------------------------------------------------------------------- +void vtkMRMLGUIWidgetsDisplayableManager::RemoveObserversFromInteractionNode() +{ + if (!this->GetMRMLScene()) + { + return; + } + + // find the interaction node + vtkMRMLInteractionNode *interactionNode = this->GetInteractionNode(); + if (interactionNode) + { + vtkUnObserveMRMLNodeMacro(interactionNode); + } +} +*/ +//--------------------------------------------------------------------------- +void vtkMRMLGUIWidgetsDisplayableManager::OnMRMLSceneNodeRemoved(vtkMRMLNode* node) +{ + bool modified = false; + + vtkMRMLGUIWidgetNode* guiWidgetNode = vtkMRMLGUIWidgetNode::SafeDownCast(node); + if (guiWidgetNode) + { + this->Internal->RemoveGUIWidgetNode(guiWidgetNode); + modified = true; + } + + vtkMRMLGUIWidgetDisplayNode* guiWidgetDisplayNode = vtkMRMLGUIWidgetDisplayNode::SafeDownCast(node); + if (guiWidgetDisplayNode) + { + this->Internal->RemoveDisplayNode(guiWidgetDisplayNode); + modified = true; + } + + if (modified) + { + this->RequestRender(); + } +} + +//--------------------------------------------------------------------------- +void vtkMRMLGUIWidgetsDisplayableManager::OnMRMLDisplayableNodeModifiedEvent(vtkObject* caller) +{ + vtkDebugMacro("OnMRMLDisplayableNodeModifiedEvent"); + + if (!caller) + { + vtkErrorMacro("OnMRMLDisplayableNodeModifiedEvent: Could not get caller."); + return; + } + + //vtkMRMLSliceNode* sliceNode = vtkMRMLSliceNode::SafeDownCast(caller); + //if (sliceNode) + //{ + // // the associated renderWindow is a 2D SliceView + // // this is the entry point for all events fired by one of the three sliceviews + // // (e.g. change slice number, zoom etc.) + + // // we remember that this instance of the displayableManager deals with 2D + // // this is important for widget creation etc. and save the actual SliceNode + // // because during Slicer startup the SliceViews fire events, it will be always set correctly + // this->SliceNode = sliceNode; + + // // now we call the handle for specific sliceNode actions + // this->OnMRMLSliceNodeModifiedEvent(); + + // // and exit + // return; + //} + + //TODO: This is a no-op. Needed? + vtkMRMLViewNode* viewNode = vtkMRMLViewNode::SafeDownCast(caller); + if (viewNode) + { + // the associated renderWindow is a 3D View + vtkDebugMacro("OnMRMLDisplayableNodeModifiedEvent: This displayableManager handles a ThreeD view."); + return; + } +} +/* +//--------------------------------------------------------------------------- +void vtkMRMLGUIWidgetsDisplayableManager::OnMRMLSliceNodeModifiedEvent() +{ + bool renderRequested = false; + + // run through all markup nodes in the helper + vtkMRMLGUIWidgetsDisplayableManagerHelper::DisplayNodeToWidgetIt it + = this->Internal->MarkupsDisplayNodesToWidgets.begin(); + while(it != this->Internal->MarkupsDisplayNodesToWidgets.end()) + { + // we loop through all widgets + vtkSlicerMarkupsWidget* widget = (it->second); + widget->UpdateFromMRML(this->SliceNode, vtkCommand::ModifiedEvent); + if (widget->GetNeedToRender()) + { + renderRequested = true; + widget->NeedToRenderOff(); + } + ++it; + } + + if (renderRequested) + { + this->RequestRender(); + } +} + +//--------------------------------------------------------------------------- +void vtkMRMLGUIWidgetsDisplayableManager::OnInteractorStyleEvent(int eventid) +{ + Superclass::OnInteractorStyleEvent(eventid); + +} + +//--------------------------------------------------------------------------- +vtkSlicerMarkupsWidget* vtkMRMLGUIWidgetsDisplayableManager::GetWidget(vtkMRMLMarkupsDisplayNode * node) +{ + return this->Internal->GetWidget(node); +} + +//--------------------------------------------------------------------------- +/// Check if it is the correct displayableManager +//--------------------------------------------------------------------------- +bool vtkMRMLGUIWidgetsDisplayableManager::IsCorrectDisplayableManager() +{ + vtkMRMLSelectionNode *selectionNode = this->GetMRMLApplicationLogic()->GetSelectionNode(); + if (selectionNode == nullptr) + { + vtkErrorMacro ("IsCorrectDisplayableManager: No selection node in the scene."); + return false; + } + const char* placeNodeClassName = selectionNode->GetActivePlaceNodeClassName(); + if (!placeNodeClassName) + { + return false; + } + + vtkSmartPointer node = + vtkSmartPointer::Take(this->GetMRMLScene()->CreateNodeByClass(placeNodeClassName)); + vtkMRMLMarkupsNode* markupsNode = vtkMRMLMarkupsNode::SafeDownCast(node); + if (!markupsNode) + { + return false; + } + + // the purpose of the displayableManager is hardcoded + return this->IsManageable(placeNodeClassName); + +} +//--------------------------------------------------------------------------- +bool vtkMRMLGUIWidgetsDisplayableManager::IsManageable(vtkMRMLNode* node) +{ + if (node == nullptr) + { + vtkErrorMacro("Is Manageable: invalid node."); + return false; + } + + return this->IsManageable(node->GetClassName()); +} + +//--------------------------------------------------------------------------- +bool vtkMRMLGUIWidgetsDisplayableManager::IsManageable(const char* nodeClassName) +{ + if (nodeClassName == nullptr) + { + return false; + } + + vtkSlicerMarkupsLogic* markupsLogic = + vtkSlicerMarkupsLogic::SafeDownCast(this->GetMRMLApplicationLogic()->GetModuleLogic("Markups")); + if (!markupsLogic) + { + vtkErrorMacro("Is Manageable: invalid Markups logic."); + return false; + } + + vtkSmartPointer node = + vtkSmartPointer::Take(this->GetMRMLScene()->CreateNodeByClass(nodeClassName)); + vtkMRMLMarkupsNode* markupsNode = vtkMRMLMarkupsNode::SafeDownCast(node); + if (!markupsNode) + { + return false; + } + + return markupsLogic->GetWidgetByMarkupsType(markupsNode->GetMarkupType()) ? true : false; +} + +//--------------------------------------------------------------------------- +vtkMRMLMarkupsNode* vtkMRMLGUIWidgetsDisplayableManager::CreateNewMarkupsNode( + const std::string &markupsNodeClassName) +{ + vtkMRMLMarkupsNode* markupsNode = vtkMRMLMarkupsNode::SafeDownCast( + this->GetMRMLScene()->AddNewNodeByClass(markupsNodeClassName)); + + std::string nodeName = + this->GetMRMLScene()->GenerateUniqueName(markupsNode->GetDefaultNodeNamePrefix()); + markupsNode->SetName(nodeName.c_str()); + markupsNode->AddDefaultStorageNode(); + markupsNode->CreateDefaultDisplayNodes(); + + return markupsNode; +} + +//--------------------------------------------------------------------------- +vtkSlicerMarkupsWidget* vtkMRMLGUIWidgetsDisplayableManager::FindClosestWidget(vtkMRMLInteractionEventData *callData, double &closestDistance2) +{ + vtkSlicerMarkupsWidget* closestWidget = nullptr; + closestDistance2 = VTK_DOUBLE_MAX; + + for (vtkMRMLGUIWidgetsDisplayableManagerHelper::DisplayNodeToWidgetIt widgetIterator = this->Internal->MarkupsDisplayNodesToWidgets.begin(); + widgetIterator != this->Internal->MarkupsDisplayNodesToWidgets.end(); ++widgetIterator) + { + vtkSlicerMarkupsWidget* widget = widgetIterator->second; + if (!widget) + { + continue; + } + double distance2FromWidget = VTK_DOUBLE_MAX; + if (widget->CanProcessInteractionEvent(callData, distance2FromWidget)) + { + if (!closestWidget || distance2FromWidget < closestDistance2) + { + closestDistance2 = distance2FromWidget; + closestWidget = widget; + } + } + } + return closestWidget; +} + +//--------------------------------------------------------------------------- +bool vtkMRMLGUIWidgetsDisplayableManager::CanProcessInteractionEvent(vtkMRMLInteractionEventData* eventData, double &closestDistance2) +{ + vtkMRMLInteractionNode* interactionNode = this->GetInteractionNode(); + // New point can be placed anywhere + int eventid = eventData->GetType(); + // We allow mouse move with the shift modifier to be processed while in place mode so that we can continue to update the + // preview positionm, even when using shift + mouse-move to adjust the crosshair position. + if ((eventid == vtkCommand::MouseMoveEvent + && (eventData->GetModifiers() == vtkEvent::NoModifier || + (eventData->GetModifiers() & vtkEvent::ShiftModifier && + interactionNode && interactionNode->GetCurrentInteractionMode() == vtkMRMLInteractionNode::Place))) + || eventid == vtkCommand::Move3DEvent + //|| (eventid == vtkCommand::LeftButtonPressEvent && eventData->GetModifiers() == vtkEvent::NoModifier) + //|| eventid == vtkCommand::LeftButtonReleaseEvent + //|| eventid == vtkCommand::RightButtonReleaseEvent + //|| eventid == vtkCommand::EnterEvent + //|| eventid == vtkCommand::LeaveEvent + ) + { + vtkMRMLSelectionNode *selectionNode = this->GetSelectionNode(); + if (!interactionNode || !selectionNode) + { + return false; + } + if (interactionNode->GetCurrentInteractionMode() == vtkMRMLInteractionNode::Place + && this->IsManageable(selectionNode->GetActivePlaceNodeClassName())) + { + + // If there is a suitable markups node for placement but it is not available in current view + // then we do not allow placement (placement would create a new markup node for this view, + // which would probably not what users want - they would like to place using the current markups node) + bool canPlaceInThisView = false; + vtkMRMLMarkupsNode* markupsNode = this->GetActiveMarkupsNodeForPlacement(); + if (markupsNode) + { + int numberOfDisplayNodes = markupsNode->GetNumberOfDisplayNodes(); + vtkMRMLAbstractViewNode* viewNode = vtkMRMLAbstractViewNode::SafeDownCast(this->GetMRMLDisplayableNode()); + for (int displayNodeIndex = 0; displayNodeIndex < numberOfDisplayNodes; displayNodeIndex++) + { + vtkMRMLDisplayNode* displayNode = markupsNode->GetNthDisplayNode(displayNodeIndex); + if (displayNode && displayNode->IsDisplayableInView(viewNode->GetID())) + { + canPlaceInThisView = true; + break; + } + } + } + else + { + // a new markups node will be created + canPlaceInThisView = true; + } + if (canPlaceInThisView) + { + closestDistance2 = 0.0; + return true; + } + } + } + + if (eventid == vtkCommand::LeaveEvent && this->LastActiveWidget != nullptr) + { + if (this->LastActiveWidget->GetMarkupsDisplayNode() && this->LastActiveWidget->GetMarkupsDisplayNode()->HasActiveComponent()) + { + // this widget has active component, therefore leave event is relevant + closestDistance2 = 0.0; + return this->LastActiveWidget; + } + } + + // Other interactions + bool canProcess = (this->FindClosestWidget(eventData, closestDistance2) != nullptr); + + if (!canProcess && this->LastActiveWidget != nullptr + && (eventid == vtkCommand::MouseMoveEvent || eventid == vtkCommand::Move3DEvent) ) + { + // interaction context (e.g. mouse) is moved away from the widget -> deactivate if it's the same context that activated it + std::vector contextsWithActiveComponents = + this->LastActiveWidget->GetMarkupsDisplayNode()->GetActiveComponentInteractionContexts(); + if (std::find(contextsWithActiveComponents.begin(), contextsWithActiveComponents.end(), eventData->GetInteractionContextName()) + != contextsWithActiveComponents.end() ) + { + this->LastActiveWidget->Leave(eventData); + this->LastActiveWidget = nullptr; + } + } + + return canProcess; +} + +//--------------------------------------------------------------------------- +bool vtkMRMLGUIWidgetsDisplayableManager::ProcessInteractionEvent(vtkMRMLInteractionEventData* eventData) +{ + if (this->GetDisableInteractorStyleEventsProcessing()) + { + return false; + } + int eventid = eventData->GetType(); + + if (eventid == vtkCommand::LeaveEvent) + { + if (this->LastActiveWidget != nullptr) + { + this->LastActiveWidget->Leave(eventData); + this->LastActiveWidget = nullptr; + } + } + + // Find/create active widget + vtkSlicerMarkupsWidget* activeWidget = nullptr; + if (this->GetInteractionNode()->GetCurrentInteractionMode() == vtkMRMLInteractionNode::Place) + { + activeWidget = this->GetWidgetForPlacement(); + if (activeWidget) + { + activeWidget->SetWidgetState(vtkSlicerMarkupsWidget::WidgetStateDefine); + } + } + else + { + double closestDistance2 = VTK_DOUBLE_MAX; + activeWidget = this->FindClosestWidget(eventData, closestDistance2); + } + + // Deactivate previous widget + if (this->LastActiveWidget != nullptr && this->LastActiveWidget != activeWidget) + { + this->LastActiveWidget->Leave(eventData); + } + this->LastActiveWidget = activeWidget; + if (!activeWidget) + { + // deactivate widget if we move far from it + if (eventid == vtkCommand::MouseMoveEvent && this->LastActiveWidget != nullptr) + { + this->LastActiveWidget->Leave(eventData); + this->LastActiveWidget = nullptr; + } + return false; + } + + // Pass on the interaction event to the active widget + return activeWidget->ProcessInteractionEvent(eventData); +} + +//--------------------------------------------------------------------------- +vtkMRMLMarkupsNode* vtkMRMLGUIWidgetsDisplayableManager::GetActiveMarkupsNodeForPlacement() +{ + vtkMRMLSelectionNode *selectionNode = this->GetSelectionNode(); + if (!selectionNode) + { + return nullptr; + } + const char *activeMarkupsID = selectionNode->GetActivePlaceNodeID(); + vtkMRMLMarkupsNode *markupsNode = vtkMRMLMarkupsNode::SafeDownCast(this->GetMRMLScene()->GetNodeByID(activeMarkupsID)); + if (!markupsNode) + { + return nullptr; + } + // Additional checks for placement + const char* placeNodeClassName = selectionNode->GetActivePlaceNodeClassName(); + if (!placeNodeClassName) + { + return nullptr; + } + if (!this->IsManageable(placeNodeClassName)) + { + return nullptr; + } + if (std::string(markupsNode->GetClassName()) != placeNodeClassName) + { + return nullptr; + } + return markupsNode; +} + +//--------------------------------------------------------------------------- +int vtkMRMLGUIWidgetsDisplayableManager::GetCurrentInteractionMode() +{ + vtkMRMLInteractionNode *interactionNode = this->GetInteractionNode(); + if (!interactionNode) + { + return 0; + } + return interactionNode->GetCurrentInteractionMode(); +} + +//--------------------------------------------------------------------------- +vtkSlicerMarkupsWidget* vtkMRMLGUIWidgetsDisplayableManager::GetWidgetForPlacement() +{ + if (this->GetCurrentInteractionMode() != vtkMRMLInteractionNode::Place) + { + return nullptr; + } + vtkMRMLSelectionNode *selectionNode = this->GetSelectionNode(); + if (!selectionNode) + { + return nullptr; + } + std::string placeNodeClassName = (selectionNode->GetActivePlaceNodeClassName() ? selectionNode->GetActivePlaceNodeClassName() : nullptr); + if (!this->IsManageable(placeNodeClassName.c_str())) + { + return nullptr; + } + + // Check if the active markups node is already the right class, and if yes then use that + vtkMRMLMarkupsNode *activeMarkupsNode = this->GetActiveMarkupsNodeForPlacement(); + + // Do not create a new widget if the markup is not displayable in this view + if (activeMarkupsNode) + { + bool canPlaceInThisView = false; + int numberOfDisplayNodes = activeMarkupsNode->GetNumberOfDisplayNodes(); + vtkMRMLAbstractViewNode* viewNode = vtkMRMLAbstractViewNode::SafeDownCast(this->GetMRMLDisplayableNode()); + for (int displayNodeIndex = 0; displayNodeIndex < numberOfDisplayNodes; displayNodeIndex++) + { + vtkMRMLDisplayNode* displayNode = activeMarkupsNode->GetNthDisplayNode(displayNodeIndex); + if (displayNode && displayNode->IsDisplayableInView(viewNode->GetID())) + { + canPlaceInThisView = true; + break; + } + } + if (!canPlaceInThisView) + { + return nullptr; + } + } + + if (activeMarkupsNode && activeMarkupsNode->GetMaximumNumberOfControlPoints() > 0 + && activeMarkupsNode->GetNumberOfControlPoints() >= activeMarkupsNode->GetMaximumNumberOfControlPoints()) + { + // maybe reached maximum number of points - if yes, then create a new widget + if (activeMarkupsNode->GetNumberOfControlPoints() == activeMarkupsNode->GetMaximumNumberOfControlPoints()) + { + // one more point than the maximum + vtkSlicerMarkupsWidget *slicerWidget = this->Internal->GetWidget(activeMarkupsNode); + if (slicerWidget && !slicerWidget->IsPointPreviewed()) + { + // no preview is shown, so the widget is actually complete + activeMarkupsNode = nullptr; + } + } + else + { + // clearly over the maximum number of points + activeMarkupsNode = nullptr; + } + } + + // If there is no active markups node then create a new one + if (!activeMarkupsNode) + { + activeMarkupsNode = this->CreateNewMarkupsNode(placeNodeClassName); + selectionNode->SetReferenceActivePlaceNodeID(activeMarkupsNode->GetID()); + } + + if (!activeMarkupsNode) + { + return nullptr; + } + vtkSlicerMarkupsWidget *slicerWidget = this->Internal->GetWidget(activeMarkupsNode); + return slicerWidget; +} + +//--------------------------------------------------------------------------- +void vtkMRMLGUIWidgetsDisplayableManager::SetHasFocus(bool hasFocus) +{ + if (!hasFocus && this->LastActiveWidget!=nullptr) + { + this->LastActiveWidget->Leave(nullptr); + this->LastActiveWidget = nullptr; + } +} + +//--------------------------------------------------------------------------- +bool vtkMRMLGUIWidgetsDisplayableManager::GetGrabFocus() +{ + return (this->LastActiveWidget != nullptr && this->LastActiveWidget->GetGrabFocus()); +} + +//--------------------------------------------------------------------------- +bool vtkMRMLGUIWidgetsDisplayableManager::GetInteractive() +{ + return (this->LastActiveWidget != nullptr && this->LastActiveWidget->GetInteractive()); +} + +//--------------------------------------------------------------------------- +int vtkMRMLGUIWidgetsDisplayableManager::GetMouseCursor() +{ + if (!this->LastActiveWidget) + { + return VTK_CURSOR_DEFAULT; + } + return this->LastActiveWidget->GetMouseCursor(); +} +*/ +//--------------------------------------------------------------------------- +vtkSlicerQWidgetWidget* vtkMRMLGUIWidgetsDisplayableManager::CreateWidget(vtkMRMLGUIWidgetDisplayNode* widgetDisplayNode) +{ + vtkSlicerQWidgetWidget* widget = vtkSlicerQWidgetWidget::New(); + + //TODO: Display node is not used, remove from attribute? + + // If the widget was successfully created + if (widget) + { + vtkMRMLAbstractViewNode* viewNode = vtkMRMLAbstractViewNode::SafeDownCast(this->GetMRMLDisplayableNode()); + vtkRenderer* renderer = this->GetRenderer(); + widget->SetMRMLApplicationLogic(this->GetMRMLApplicationLogic()); + widget->CreateDefaultRepresentation(/*widgetDisplayNode, */viewNode, renderer); + } + + return widget; +} +/* +//--------------------------------------------------------------------------- +void vtkMRMLGUIWidgetsDisplayableManager::ConvertDeviceToXYZ(double x, double y, double xyz[3]) +{ + vtkMRMLAbstractSliceViewDisplayableManager::ConvertDeviceToXYZ(this->GetInteractor(), this->GetMRMLSliceNode(), x, y, xyz); +} +*/ diff --git a/GUIWidgets/MRMLDM/vtkMRMLGUIWidgetsDisplayableManager.h b/GUIWidgets/MRMLDM/vtkMRMLGUIWidgetsDisplayableManager.h new file mode 100644 index 0000000..51f24c7 --- /dev/null +++ b/GUIWidgets/MRMLDM/vtkMRMLGUIWidgetsDisplayableManager.h @@ -0,0 +1,177 @@ +/*============================================================================== + + Program: 3D Slicer + + Portions (c) Copyright Brigham and Women's Hospital (BWH) All Rights Reserved. + + See COPYRIGHT.txt + or http://www.slicer.org/copyright/copyright.txt for details. + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + This file was originally developed by Csaba Pinter, EBATINCA, S.L., and + development was supported by "ICEX Espana Exportacion e Inversiones" under + the program "Inversiones de Empresas Extranjeras en Actividades de I+D + (Fondo Tecnologico)- Convocatoria 2021" + +==============================================================================*/ + +#ifndef __vtkMRMLGUIWidgetsDisplayableManager_h +#define __vtkMRMLGUIWidgetsDisplayableManager_h + +// GUIWidget includes +#include "vtkSlicerGUIWidgetsModuleMRMLDisplayableManagerExport.h" + +// GUIWidgetsModule/MRMLDisplayableManager includes +//#include "vtkMRMLGUIWidgetsDisplayableManagerHelper.h" + +// MRMLDisplayableManager includes +#include + +#include + +// STD includes +#include + +class vtkMRMLGUIWidgetNode; +class vtkSlicerViewerWidget; +class vtkMRMLGUIWidgetDisplayNode; +class vtkAbstractWidget; + +/// \ingroup Slicer_QtModules_GUIWidgets +class VTK_SLICER_GUIWIDGETS_MODULE_MRMLDISPLAYABLEMANAGER_EXPORT vtkMRMLGUIWidgetsDisplayableManager + : public vtkMRMLAbstractDisplayableManager +{ +public: + + // Allow the helper to call protected methods of displayable manager + //friend class vtkMRMLGUIWidgetsDisplayableManagerHelper; + + static vtkMRMLGUIWidgetsDisplayableManager *New(); + vtkTypeMacro(vtkMRMLGUIWidgetsDisplayableManager, vtkMRMLAbstractDisplayableManager); + void PrintSelf(ostream& os, vtkIndent indent) override; + /* + /// Check if this is a 2d SliceView displayable manager, returns true if so, + /// false otherwise. Checks return from GetSliceNode for non null, which means + /// it's a 2d displayable manager + virtual bool Is2DDisplayableManager(); + + /// Get the sliceNode, if registered. This would mean it is a 2D SliceView displayableManager. + vtkMRMLSliceNode * GetMRMLSliceNode(); + + vtkMRMLGUIWidgetsDisplayableManagerHelper * GetHelper() { return this->Helper; }; + + bool CanProcessInteractionEvent(vtkMRMLInteractionEventData* eventData, double &closestDistance2) override; + bool ProcessInteractionEvent(vtkMRMLInteractionEventData* eventData) override; + + void SetHasFocus(bool hasFocus) override; + bool GetGrabFocus() override; + bool GetInteractive() override; + int GetMouseCursor() override; + + // Updates markup point preview position. + // Returns true if the event is processed. + vtkSlicerMarkupsWidget* GetWidgetForPlacement(); + + vtkMRMLMarkupsNode* GetActiveMarkupsNodeForPlacement(); + + int GetCurrentInteractionMode(); + + // Methods from vtkMRMLAbstractSliceViewDisplayableManager + + /// Convert device coordinates (display) to XYZ coordinates (viewport). + /// Parameter \a xyz is double[3] + /// \sa ConvertDeviceToXYZ(vtkRenderWindowInteractor *, vtkMRMLSliceNode *, double x, double y, double xyz[3]) + void ConvertDeviceToXYZ(double x, double y, double xyz[3]); + + /// Get the widget of a node. + vtkSlicerMarkupsWidget* GetWidget(vtkMRMLMarkupsDisplayNode * node); + */ +protected: + + vtkMRMLGUIWidgetsDisplayableManager(); + ~vtkMRMLGUIWidgetsDisplayableManager() override; + /* + vtkSlicerMarkupsWidget* FindClosestWidget(vtkMRMLInteractionEventData *callData, double &closestDistance2); + */ + void ProcessMRMLNodesEvents(vtkObject *caller, unsigned long event, void *callData) override; + + /// Wrap the superclass render request in a check for batch processing + virtual void RequestRender(); + + /// Called from RequestRender method if UpdateFromMRMLRequested is true + /// \sa RequestRender() SetUpdateFromMRMLRequested() + void UpdateFromMRML() override; + + void SetMRMLSceneInternal(vtkMRMLScene* newScene) override; + + /// Called after the corresponding MRML event is triggered, from AbstractDisplayableManager + /// \sa ProcessMRMLSceneEvents + void UpdateFromMRMLScene() override; + void OnMRMLSceneEndClose() override; + void OnMRMLSceneEndImport() override; + void OnMRMLSceneNodeAdded(vtkMRMLNode* node) override; + void OnMRMLSceneNodeRemoved(vtkMRMLNode* node) override; + + /// Create a widget. + vtkSlicerQWidgetWidget* CreateWidget(vtkMRMLGUIWidgetDisplayNode* node); + + /// Called after the corresponding MRML View container was modified + void OnMRMLDisplayableNodeModifiedEvent(vtkObject* caller) override; + /* + /// Handler for specific SliceView actions, iterate over the widgets in the helper + virtual void OnMRMLSliceNodeModifiedEvent(); + + /// Observe the interaction node. + void AddObserversToInteractionNode(); + void RemoveObserversFromInteractionNode(); + + /// Check if it is the right displayManager + virtual bool IsCorrectDisplayableManager(); + + /// Return true if this displayable manager supports(can manage) that node, + /// false otherwise. + /// Can be reimplemented to add more conditions. + /// \sa IsManageable(const char*), IsCorrectDisplayableManager() + virtual bool IsManageable(vtkMRMLNode* node); + /// Return true if this displayable manager supports(can manage) that node class, + /// false otherwise. + /// Can be reimplemented to add more conditions. + /// \sa IsManageable(vtkMRMLNode*), IsCorrectDisplayableManager() + virtual bool IsManageable(const char* nodeClassName); + + /// Respond to interactor style events + void OnInteractorStyleEvent(int eventid) override; + + /// Accessor for internal flag that disables interactor style event processing + vtkGetMacro(DisableInteractorStyleEventsProcessing, int); + + vtkSmartPointer Helper; + + double LastClickWorldCoordinates[4]; + + vtkMRMLMarkupsNode* CreateNewMarkupsNode(const std::string &markupsNodeClassName); + + vtkWeakPointer LastActiveWidget; + */ +private: + vtkMRMLGUIWidgetsDisplayableManager(const vtkMRMLGUIWidgetsDisplayableManager&) = delete; + void operator=(const vtkMRMLGUIWidgetsDisplayableManager&) = delete; + /* + int DisableInteractorStyleEventsProcessing; + + // by default, this displayableManager handles a 2d view, so the SliceNode + // must be set when it's assigned to a viewer + vtkWeakPointer SliceNode; + */ + + class vtkInternal; + vtkInternal* Internal; + friend class vtkInternal; +}; + +#endif diff --git a/GUIWidgets/Resources/Icons/GUIWidgets.png b/GUIWidgets/Resources/Icons/GUIWidgets.png new file mode 100644 index 0000000..9c5938e Binary files /dev/null and b/GUIWidgets/Resources/Icons/GUIWidgets.png differ diff --git a/GUIWidgets/Resources/UI/qSlicerGUIWidgetsModuleWidget.ui b/GUIWidgets/Resources/UI/qSlicerGUIWidgetsModuleWidget.ui new file mode 100644 index 0000000..5b06e33 --- /dev/null +++ b/GUIWidgets/Resources/UI/qSlicerGUIWidgetsModuleWidget.ui @@ -0,0 +1,158 @@ + + + qSlicerGUIWidgetsModuleWidget + + + + 0 + 0 + 342 + 514 + + + + vtkQWidgetWidget test + + + + + + Test GUIWidget + + + + + + Add GUI widget node with Hello World push button + + + + + + + + + + + + To test rendering update on change in widget + + + Update button label + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + Add GUI widget node with Home widget + + + + + + + Add GUI widget node with Data Module widget + + + + + + + Add GUI widget node with Segment Editor widget + + + + + + + Add GUI widget node with Transform widget + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + VR pointer development + + + + + + Set up interaction + + + + + + + Start interaction + + + + + + + + + + + + + Qt::Vertical + + + + 0 + 0 + + + + + + + + + qSlicerWidget + QWidget +
qSlicerWidget.h
+ 1 +
+ + ctkCollapsibleButton + QWidget +
ctkCollapsibleButton.h
+ 1 +
+
+ + +
diff --git a/GUIWidgets/Resources/qSlicerGUIWidgetsModule.qrc b/GUIWidgets/Resources/qSlicerGUIWidgetsModule.qrc new file mode 100644 index 0000000..f5e54d6 --- /dev/null +++ b/GUIWidgets/Resources/qSlicerGUIWidgetsModule.qrc @@ -0,0 +1,5 @@ + + + Icons/GUIWidgets.png + + diff --git a/GUIWidgets/Testing/CMakeLists.txt b/GUIWidgets/Testing/CMakeLists.txt new file mode 100644 index 0000000..35f9732 --- /dev/null +++ b/GUIWidgets/Testing/CMakeLists.txt @@ -0,0 +1 @@ +add_subdirectory(Cxx) diff --git a/GUIWidgets/Testing/Cxx/CMakeLists.txt b/GUIWidgets/Testing/Cxx/CMakeLists.txt new file mode 100644 index 0000000..b7341cd --- /dev/null +++ b/GUIWidgets/Testing/Cxx/CMakeLists.txt @@ -0,0 +1,17 @@ +set(KIT qSlicer${MODULE_NAME}Module) + +#----------------------------------------------------------------------------- +set(KIT_TEST_SRCS + #qSlicer${MODULE_NAME}ModuleTest.cxx + ) + +#----------------------------------------------------------------------------- +slicerMacroConfigureModuleCxxTestDriver( + NAME ${KIT} + SOURCES ${KIT_TEST_SRCS} + WITH_VTK_DEBUG_LEAKS_CHECK + WITH_VTK_ERROR_OUTPUT_CHECK + ) + +#----------------------------------------------------------------------------- +#simple_test(qSlicer${MODULE_NAME}ModuleTest) diff --git a/GUIWidgets/VTKWidgets/CMakeLists.txt b/GUIWidgets/VTKWidgets/CMakeLists.txt new file mode 100644 index 0000000..2dcd655 --- /dev/null +++ b/GUIWidgets/VTKWidgets/CMakeLists.txt @@ -0,0 +1,34 @@ +project(vtkSlicer${MODULE_NAME}ModuleVTKWidgets) + +set(KIT ${PROJECT_NAME}) + +set(${KIT}_EXPORT_DIRECTIVE "VTK_SLICER_${MODULE_NAME_UPPER}_MODULE_VTKWIDGETS_EXPORT") + +set(${KIT}_INCLUDE_DIRECTORIES + ${vtkSlicer${MODULE_NAME}ModuleMRML_SOURCE_DIR} + ${vtkSlicer${MODULE_NAME}ModuleMRML_BINARY_DIR} + ${vtkSlicerMarkupsModuleVTKWidgets_INCLUDE_DIRS} + ) + +set(${KIT}_SRCS + vtkSlicerQWidgetRepresentation.cxx + vtkSlicerQWidgetRepresentation.h + vtkSlicerQWidgetTexture.cxx + vtkSlicerQWidgetTexture.h + vtkSlicerQWidgetWidget.cxx + vtkSlicerQWidgetWidget.h + ) + +set(${KIT}_TARGET_LIBRARIES + vtkSlicer${MODULE_NAME}ModuleMRML + vtkSlicerMarkupsModuleVTKWidgets + ) + +#----------------------------------------------------------------------------- +SlicerMacroBuildModuleLogic( + NAME ${KIT} + EXPORT_DIRECTIVE ${${KIT}_EXPORT_DIRECTIVE} + INCLUDE_DIRECTORIES ${${KIT}_INCLUDE_DIRECTORIES} + SRCS ${${KIT}_SRCS} + TARGET_LIBRARIES ${${KIT}_TARGET_LIBRARIES} + ) diff --git a/GUIWidgets/VTKWidgets/vtkSlicerQWidgetRepresentation.cxx b/GUIWidgets/VTKWidgets/vtkSlicerQWidgetRepresentation.cxx new file mode 100644 index 0000000..856a999 --- /dev/null +++ b/GUIWidgets/VTKWidgets/vtkSlicerQWidgetRepresentation.cxx @@ -0,0 +1,274 @@ +/*============================================================================== + + Program: 3D Slicer + + Portions (c) Copyright Brigham and Women's Hospital (BWH) All Rights Reserved. + + See COPYRIGHT.txt + or http://www.slicer.org/copyright/copyright.txt for details. + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + This file was originally developed by Csaba Pinter, EBATINCA, S.L., and + development was supported by "ICEX Espana Exportacion e Inversiones" under + the program "Inversiones de Empresas Extranjeras en Actividades de I+D + (Fondo Tecnologico)- Convocatoria 2021" + +==============================================================================*/ + +#include "vtkSlicerQWidgetRepresentation.h" + +#include "vtkSlicerQWidgetTexture.h" + +// GUI Widget includes +#include "vtkMRMLGUIWidgetNode.h" +#include "vtkMRMLGUIWidgetDisplayNode.h" + +// VTK includes +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// Qt includes +#include +#include + +vtkStandardNewMacro(vtkSlicerQWidgetRepresentation); + +//------------------------------------------------------------------------------ +vtkSlicerQWidgetRepresentation::vtkSlicerQWidgetRepresentation() +{ + this->PlaneSource = vtkPlaneSource::New(); + this->PlaneSource->SetOutputPointsPrecision(vtkAlgorithm::DOUBLE_PRECISION); + + this->PlaneMapper = vtkPolyDataMapper::New(); + this->PlaneMapper->SetInputConnection(this->PlaneSource->GetOutputPort()); + + this->TextureCallbackCommand = vtkCallbackCommand::New(); + this->TextureCallbackCommand->SetClientData( reinterpret_cast(this) ); + this->TextureCallbackCommand->SetCallback( vtkSlicerQWidgetRepresentation::OnTextureModified ); + + this->QWidgetTexture = vtkSlicerQWidgetTexture::New(); + this->QWidgetTexture->AddObserver(vtkCommand::ModifiedEvent, this->TextureCallbackCommand); + + this->PlaneActor = vtkActor::New(); + this->PlaneActor->SetMapper(this->PlaneMapper); + this->PlaneActor->SetTexture(this->QWidgetTexture); + + this->PlaneActor->GetProperty()->SetAmbient(1.0); + this->PlaneActor->GetProperty()->SetDiffuse(0.0); + + // Define the point coordinates + double bounds[6] = {-80, 80, -0.5, 0.5, -50, 50 }; // Width, Depth, Height + + // Initial creation of the widget, serves to initialize it + this->PlaceWidget(bounds); +} + +//------------------------------------------------------------------------------ +vtkSlicerQWidgetRepresentation::~vtkSlicerQWidgetRepresentation() +{ + if (this->PlaneSource) + { + this->PlaneSource->Delete(); + this->PlaneSource = nullptr; + } + if (this->PlaneMapper) + { + this->PlaneMapper->Delete(); + this->PlaneMapper = nullptr; + } + if (this->PlaneActor) + { + this->PlaneActor->Delete(); + this->PlaneActor = nullptr; + } + + if (this->QWidgetTexture) + { + this->QWidgetTexture->RemoveObserver(this->TextureCallbackCommand); + this->QWidgetTexture->Delete(); + this->QWidgetTexture = nullptr; + } + if (this->TextureCallbackCommand) + { + this->TextureCallbackCommand->SetClientData(nullptr); + this->TextureCallbackCommand->Delete(); + this->TextureCallbackCommand = nullptr; + } +} + +//------------------------------------------------------------------------------ +void vtkSlicerQWidgetRepresentation::SetWidget(QWidget* w) +{ + // just pass down to the QWidgetTexture + this->QWidgetTexture->SetWidget(w); + this->Modified(); +} + +//------------------------------------------------------------------------------ +double* vtkSlicerQWidgetRepresentation::GetBounds() +{ + //this->BuildRepresentation(); + return this->PlaneActor->GetBounds(); +} + +//------------------------------------------------------------------------------ +void vtkSlicerQWidgetRepresentation::GetActors(vtkPropCollection* pc) +{ + this->Superclass::GetActors(pc); + this->PlaneActor->GetActors(pc); +} + +//------------------------------------------------------------------------------ +void vtkSlicerQWidgetRepresentation::ReleaseGraphicsResources(vtkWindow* w) +{ + this->Superclass::ReleaseGraphicsResources(w); + this->PlaneActor->ReleaseGraphicsResources(w); + this->PlaneMapper->ReleaseGraphicsResources(w); + this->QWidgetTexture->ReleaseGraphicsResources(w); +} + +//------------------------------------------------------------------------------ +int vtkSlicerQWidgetRepresentation::RenderOpaqueGeometry(vtkViewport* v) +{ + int count = this->Superclass::RenderOpaqueGeometry(v); + + if (this->PlaneActor->GetVisibility()) + { + this->PlaneActor->SetPropertyKeys(this->GetPropertyKeys()); + + count += this->PlaneActor->RenderOpaqueGeometry(v); + } + + return count; +} + +//------------------------------------------------------------------------------ +int vtkSlicerQWidgetRepresentation::RenderTranslucentPolygonalGeometry(vtkViewport* viewport) +{ + int count=0; + count = this->Superclass::RenderTranslucentPolygonalGeometry(viewport); + if (this->PlaneActor->GetVisibility()) + { + // The internal actor needs to share property keys. + // This ensures the mapper state is consistent and allows depth peeling to work as expected. + this->PlaneActor->SetPropertyKeys(this->GetPropertyKeys()); + + count += this->PlaneActor->RenderTranslucentPolygonalGeometry(viewport); + } + return count; +} + +//------------------------------------------------------------------------------ +vtkTypeBool vtkSlicerQWidgetRepresentation::HasTranslucentPolygonalGeometry() +{ + if (this->Superclass::HasTranslucentPolygonalGeometry()) + { + return true; + } + if (this->PlaneActor->GetVisibility() && this->PlaneActor->HasTranslucentPolygonalGeometry()) + { + return true; + } + return false; +} + +//------------------------------------------------------------------------------ +void vtkSlicerQWidgetRepresentation::PrintSelf(ostream& os, vtkIndent indent) +{ + this->Superclass::PrintSelf(os, indent); + + // this->InteractionState is printed in superclass + // this is commented to avoid PrintSelf errors +} + +//------------------------------------------------------------------------------ +void vtkSlicerQWidgetRepresentation::PlaceWidget(double bds[6]) +{ + this->PlaneSource->SetOrigin(bds[0], bds[2], bds[4]); + this->PlaneSource->SetPoint1(bds[1], bds[2], bds[4]); + this->PlaneSource->SetPoint2(bds[0], bds[2], bds[5]); +} + +//---------------------------------------------------------------------- +void vtkSlicerQWidgetRepresentation::UpdateFromMRML(vtkMRMLNode* caller, unsigned long event, void *callData /*=nullptr*/) +{ + Superclass::UpdateFromMRML(caller, event, callData); + + this->NeedToRenderOn(); + + vtkMRMLGUIWidgetNode* guiWidgetNode = vtkMRMLGUIWidgetNode::SafeDownCast(this->GetMarkupsNode()); + vtkMRMLGUIWidgetDisplayNode* displayNode = vtkMRMLGUIWidgetDisplayNode::SafeDownCast(this->GetMarkupsDisplayNode()); + if (!guiWidgetNode || !this->IsDisplayable() || !displayNode) + { + this->VisibilityOff(); + return; + } + + if (guiWidgetNode->GetWidget() != this->GetQWidgetTexture()->GetWidget()) + { + if (guiWidgetNode->GetWidget() == nullptr) + { + this->QWidgetTexture->SetWidget(nullptr); + } + else + { + QWidget* widget = reinterpret_cast(guiWidgetNode->GetWidget()); + if (widget != this->QWidgetTexture->GetWidget()) + { + this->QWidgetTexture->SetWidget(widget); + } + } + } + + if (!this->QWidgetTexture->GetWidget() || !this->ViewNode) + { + this->VisibilityOff(); + this->PlaneActor->SetVisibility(false); + return; + } + + this->VisibilityOn(); + this->PlaneActor->SetVisibility(true); +} + +//--------------------------------------------------------------------------- +void vtkSlicerQWidgetRepresentation::OnTextureModified( + vtkObject* vtkNotUsed(caller), unsigned long vtkNotUsed(eid), void* clientData, void* vtkNotUsed(callData)) +{ + vtkSlicerQWidgetRepresentation* self = reinterpret_cast(clientData); + + // Redefine widget plane + QWidget* widget = self->QWidgetTexture->GetWidget(); + if (!widget) + { + return; + } + + QRect rect = widget->geometry(); + if (rect.width() < 2 || rect.height() < 2) + { + return; + } + double bounds[6] = { + -(double)(rect.width()/2)*self->SpacingMmPerPixel, (double)rect.width()/2*self->SpacingMmPerPixel, + -0.5, 0.5, + -(double)(rect.height()/2)*self->SpacingMmPerPixel, (double)rect.height()/2*self->SpacingMmPerPixel + }; + self->PlaceWidget(bounds); + + // Trigger rendering in view + self->GetViewNode()->Modified(); +} diff --git a/GUIWidgets/VTKWidgets/vtkSlicerQWidgetRepresentation.h b/GUIWidgets/VTKWidgets/vtkSlicerQWidgetRepresentation.h new file mode 100644 index 0000000..79d5c5a --- /dev/null +++ b/GUIWidgets/VTKWidgets/vtkSlicerQWidgetRepresentation.h @@ -0,0 +1,132 @@ +/*============================================================================== + + Program: 3D Slicer + + Portions (c) Copyright Brigham and Women's Hospital (BWH) All Rights Reserved. + + See COPYRIGHT.txt + or http://www.slicer.org/copyright/copyright.txt for details. + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + This file was originally developed by Csaba Pinter, EBATINCA, S.L., and + development was supported by "ICEX Espana Exportacion e Inversiones" under + the program "Inversiones de Empresas Extranjeras en Actividades de I+D + (Fondo Tecnologico)- Convocatoria 2021" + +==============================================================================*/ + +/** + * @class vtkSlicerQWidgetRepresentation + * @brief a class defining the representation for a vtkSlicerQWidgetWidget + * + * This class renders a QWidget as a simple vtkPlaneSource with a vtkTexture + * that contains a vtkSlicerQWidgetTexture which imports the OpenGL texture handle + * from Qt into the vtk scene. Qt and VTK may need to be using the same + * graphics context. + */ + +#ifndef vtkSlicerQWidgetRepresentation_h +#define vtkSlicerQWidgetRepresentation_h + +#include "vtkSlicerGUIWidgetsModuleVTKWidgetsExport.h" + +#include "vtkSlicerMarkupsWidgetRepresentation3D.h" + +class QWidget; + +class vtkActor; +class vtkCallbackCommand; +class vtkOpenGLTexture; +class vtkPlaneSource; +class vtkPolyDataAlgorithm; +class vtkPolyDataMapper; +class vtkSlicerQWidgetTexture; + +class VTK_SLICER_GUIWIDGETS_MODULE_VTKWIDGETS_EXPORT vtkSlicerQWidgetRepresentation : public vtkSlicerMarkupsWidgetRepresentation3D +{ +public: + /** + * Instantiate the class. + */ + static vtkSlicerQWidgetRepresentation* New(); + + ///@{ + /// Standard methods for the class. + vtkTypeMacro(vtkSlicerQWidgetRepresentation, vtkSlicerMarkupsWidgetRepresentation3D); + void PrintSelf(ostream& os, vtkIndent indent) override; + ///@} + + /// Subclasses of vtkMRMLAbstractWidgetRepresentation must implement these methods. These + /// are the methods that the widget and its representation use to + /// communicate with each other. + void UpdateFromMRML(vtkMRMLNode* caller, unsigned long event, void *callData=nullptr) override; + + /// TODO: + void PlaceWidget(double bounds[6]); + + ///@{ + /// Methods supporting the rendering process. + double* GetBounds() VTK_SIZEHINT(6) override; + void GetActors(vtkPropCollection* pc) override; + void ReleaseGraphicsResources(vtkWindow*) override; + int RenderOpaqueGeometry(vtkViewport*) override; + int RenderTranslucentPolygonalGeometry(vtkViewport*) override; + vtkTypeBool HasTranslucentPolygonalGeometry() override; + ///@} + + // Manage the state of the widget + enum _InteractionState + { + Outside = 0, + Inside + }; + + /// Set the QWidget this representation will render + void SetWidget(QWidget* w); + + /// Get the QWidgetTexture used by the representation + vtkGetObjectMacro(QWidgetTexture, vtkSlicerQWidgetTexture); + + /// Get the vtkPlaneSouce used by this representation. This can be useful + /// to set the Origin, Point1, Point2 of the plane source directly. + vtkGetObjectMacro(PlaneSource, vtkPlaneSource); + + /// Get the widget coordinates as computed in the last call to + /// ComputeComplexInteractionState. + //vtkGetVector2Macro(WidgetCoordinates, int); + + /// Get spacing of the widget (mm/pixel) + vtkGetMacro(SpacingMmPerPixel, double); + /// Set spacing of the widget (mm/pixel) + vtkSetMacro(SpacingMmPerPixel, double); + +protected: + /// Callback function observing texture modified events. + static void OnTextureModified(vtkObject* caller, unsigned long eid, void* clientData, void* callData); + +protected: + //int WidgetCoordinates[2]; + double SpacingMmPerPixel{0.5}; + + vtkPlaneSource* PlaneSource; + vtkPolyDataMapper* PlaneMapper; + vtkActor* PlaneActor; + vtkOpenGLTexture* PlaneTexture; + vtkSlicerQWidgetTexture* QWidgetTexture; + vtkCallbackCommand* TextureCallbackCommand; + +protected: + vtkSlicerQWidgetRepresentation(); + ~vtkSlicerQWidgetRepresentation() override; + +private: + vtkSlicerQWidgetRepresentation(const vtkSlicerQWidgetRepresentation&) = delete; + void operator=(const vtkSlicerQWidgetRepresentation&) = delete; +}; + +#endif diff --git a/GUIWidgets/VTKWidgets/vtkSlicerQWidgetTexture.cxx b/GUIWidgets/VTKWidgets/vtkSlicerQWidgetTexture.cxx new file mode 100644 index 0000000..689716a --- /dev/null +++ b/GUIWidgets/VTKWidgets/vtkSlicerQWidgetTexture.cxx @@ -0,0 +1,126 @@ +/*============================================================================== + + Program: 3D Slicer + + Portions (c) Copyright Brigham and Women's Hospital (BWH) All Rights Reserved. + + See COPYRIGHT.txt + or http://www.slicer.org/copyright/copyright.txt for details. + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + This file was originally developed by Csaba Pinter, EBATINCA, S.L., and + development was supported by "ICEX Espana Exportacion e Inversiones" under + the program "Inversiones de Empresas Extranjeras en Actividades de I+D + (Fondo Tecnologico)- Convocatoria 2021" + +==============================================================================*/ + +#include "vtkSlicerQWidgetTexture.h" + +// SlicerQt includes +#include "qMRMLUtils.h" +#include "qSlicerCoreApplication.h" + +// VTK includes +#include + +// Qt includes +#include +#include +#include +#include + +//------------------------------------------------------------------------------ +vtkStandardNewMacro(vtkSlicerQWidgetTexture); + +//------------------------------------------------------------------------------ +vtkSlicerQWidgetTexture::vtkSlicerQWidgetTexture() +{ + this->Scene = nullptr; + this->Widget = nullptr; + + this->UpdateTextureMethod = [this]() { + if (!this->Widget) + { + return; + } + QImage grabImage(this->Widget->grab().toImage()); + qMRMLUtils::qImageToVtkImageData(grabImage, this->TextureImageData.GetPointer()); + this->Modified(); + }; +} + +//------------------------------------------------------------------------------ +vtkSlicerQWidgetTexture::~vtkSlicerQWidgetTexture() +{ + this->SetWidget(nullptr); + delete this->Scene; + this->Scene = nullptr; +} + +//------------------------------------------------------------------------------ +void vtkSlicerQWidgetTexture::PrintSelf(ostream& os, vtkIndent indent) +{ + this->Superclass::PrintSelf(os, indent); +} + +//------------------------------------------------------------------------------ +void vtkSlicerQWidgetTexture::ReleaseGraphicsResources(vtkWindow* win) +{ + this->Superclass::ReleaseGraphicsResources(win); +} + +//------------------------------------------------------------------------------ +void vtkSlicerQWidgetTexture::SetWidget(QWidget* w) +{ + if (this->Widget == w) + { + return; + } + + if (w == nullptr && this->Scene && this->Widget->graphicsProxyWidget()) + { + this->Scene->removeItem(this->Widget->graphicsProxyWidget()); + } + + this->Widget = w; + + this->SetupWidget(); + + this->Modified(); +} + +//------------------------------------------------------------------------------ +void vtkSlicerQWidgetTexture::SetupWidget() +{ + if (!this->Widget) + { + return; + } + + this->Scene = new QGraphicsScene(); + + this->Widget->move(0, 0); + this->Scene->addWidget(this->Widget); + + QObject::connect(this->Scene, &QGraphicsScene::changed, this->UpdateTextureMethod); + QObject::connect(this->Widget, &QObject::objectNameChanged, this->UpdateTextureMethod); //TODO: For debugging + + if (this->TextureImageData.GetPointer() == nullptr) + { + this->TextureImageData = vtkSmartPointer::New(); + } + if (this->TextureTrivialProducer.GetPointer() == nullptr) + { + this->TextureTrivialProducer = vtkSmartPointer::New(); + this->TextureTrivialProducer->SetOutput(this->TextureImageData); + this->SetInputConnection(this->TextureTrivialProducer->GetOutputPort()); + } + + this->UpdateTextureMethod(); +} diff --git a/GUIWidgets/VTKWidgets/vtkSlicerQWidgetTexture.h b/GUIWidgets/VTKWidgets/vtkSlicerQWidgetTexture.h new file mode 100644 index 0000000..d5ecdc2 --- /dev/null +++ b/GUIWidgets/VTKWidgets/vtkSlicerQWidgetTexture.h @@ -0,0 +1,95 @@ +/*============================================================================== + + Program: 3D Slicer + + Portions (c) Copyright Brigham and Women's Hospital (BWH) All Rights Reserved. + + See COPYRIGHT.txt + or http://www.slicer.org/copyright/copyright.txt for details. + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + This file was originally developed by Csaba Pinter, EBATINCA, S.L., and + development was supported by "ICEX Espana Exportacion e Inversiones" under + the program "Inversiones de Empresas Extranjeras en Actividades de I+D + (Fondo Tecnologico)- Convocatoria 2021" + +==============================================================================*/ + +#ifndef vtkSlicerQWidgetTexture_h +#define vtkSlicerQWidgetTexture_h + +#include "vtkSlicerGUIWidgetsModuleVTKWidgetsExport.h" + +// VTK includes +#include +#include +#include +#include + +#include // for ivar + +class QGraphicsScene; +class QImage; +class QWidget; + +/** + * @class vtkSlicerQWidgetTexture + * @brief Allows a QWidget to be used as a texture in VTK with OpenGL + * + * This class works by rendering the QWidget into a Framebuffer + * and then sending the OpenGL texture handle to VTK for rendering. + */ +class VTK_SLICER_GUIWIDGETS_MODULE_VTKWIDGETS_EXPORT vtkSlicerQWidgetTexture : public vtkOpenGLTexture +{ +public: + static vtkSlicerQWidgetTexture* New(); + vtkTypeMacro(vtkSlicerQWidgetTexture, vtkOpenGLTexture); + void PrintSelf(ostream& os, vtkIndent indent) override; + + ///@{ + /** + * Set/Get the QWidget that this TextureObject will render/use. + * Just hold onto the widget until opengl context is active. + */ + void SetWidget(QWidget* w); + QWidget* GetWidget() { return this->Widget; } + ///@} + + /** + * get the QScene used for rendering, this is where events will + * be forwarded to. + */ + QGraphicsScene* GetScene() { return this->Scene; } + + /** + * Free resources + */ + void ReleaseGraphicsResources(vtkWindow* win) override; + +protected: + vtkSlicerQWidgetTexture(); + ~vtkSlicerQWidgetTexture() override; + + QGraphicsScene* Scene; + QWidget* Widget; + + vtkSmartPointer TextureImageData; + vtkSmartPointer TextureTrivialProducer; + + /// method called when the widget needs repainting + std::function UpdateTextureMethod; + + /// Setup new widget with the graphics scene observation + void SetupWidget(); + +private: + vtkSlicerQWidgetTexture(const vtkSlicerQWidgetTexture&) = delete; + void operator=(const vtkSlicerQWidgetTexture&) = delete; +}; + +#endif diff --git a/GUIWidgets/VTKWidgets/vtkSlicerQWidgetWidget.cxx b/GUIWidgets/VTKWidgets/vtkSlicerQWidgetWidget.cxx new file mode 100644 index 0000000..206369e --- /dev/null +++ b/GUIWidgets/VTKWidgets/vtkSlicerQWidgetWidget.cxx @@ -0,0 +1,121 @@ +/*============================================================================== + + Program: 3D Slicer + + Portions (c) Copyright Brigham and Women's Hospital (BWH) All Rights Reserved. + + See COPYRIGHT.txt + or http://www.slicer.org/copyright/copyright.txt for details. + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + This file was originally developed by Csaba Pinter, EBATINCA, S.L., and + development was supported by "ICEX Espana Exportacion e Inversiones" under + the program "Inversiones de Empresas Extranjeras en Actividades de I+D + (Fondo Tecnologico)- Convocatoria 2021" + +==============================================================================*/ + +#include "vtkSlicerQWidgetWidget.h" + +#include "vtkMRMLGUIWidgetNode.h" + +#include "vtkSlicerQWidgetRepresentation.h" +#include "vtkSlicerQWidgetTexture.h" + +// MRML includes +#include "vtkMRMLSliceNode.h" + +// Qt includes +#include +#include +#include +#include +#include +#include + +// VTK includes +#include "vtkCallbackCommand.h" +// #include "vtkCommand.h" +#include "vtkEvent.h" +#include "vtkEventData.h" +#include "vtkObjectFactory.h" +#include "vtkRenderWindow.h" +#include "vtkRenderWindowInteractor.h" +#include "vtkRenderer.h" +#include "vtkWidgetCallbackMapper.h" +#include "vtkWidgetEvent.h" +#include "vtkWidgetEventTranslator.h" + +vtkStandardNewMacro(vtkSlicerQWidgetWidget); + +//------------------------------------------------------------------------------ +vtkSlicerQWidgetWidget::vtkSlicerQWidgetWidget() +{ +} + +//------------------------------------------------------------------------------ +vtkSlicerQWidgetWidget::~vtkSlicerQWidgetWidget() = default; + +//------------------------------------------------------------------------------ +vtkSlicerQWidgetRepresentation* vtkSlicerQWidgetWidget::GetQWidgetRepresentation() +{ + return vtkSlicerQWidgetRepresentation::SafeDownCast(this->WidgetRep); +} + +//------------------------------------------------------------------------------ +vtkSlicerMarkupsWidget* vtkSlicerQWidgetWidget::CreateInstance()const +{ + vtkObject* ret = vtkObjectFactory::CreateInstance("vtkSlicerQWidgetWidget"); + if(ret) + { + return static_cast(ret); + } + + vtkSlicerQWidgetWidget* result = new vtkSlicerQWidgetWidget; +#ifdef VTK_HAS_INITIALIZE_OBJECT_BASE + result->InitializeObjectBase(); +#endif + return result; +} + +//---------------------------------------------------------------------- +void vtkSlicerQWidgetWidget::CreateDefaultRepresentation( + vtkMRMLMarkupsDisplayNode* markupsDisplayNode, vtkMRMLAbstractViewNode* viewNode, vtkRenderer* renderer) +{ + if (vtkMRMLSliceNode::SafeDownCast(viewNode)) + { + // There is no 2D representation of the GUI widget + return; + } + + vtkNew rep; + this->SetRenderer(renderer); + this->SetRepresentation(rep); + rep->SetMarkupsDisplayNode(markupsDisplayNode); + rep->SetViewNode(viewNode); + + rep->UpdateFromMRML(nullptr, 0); // full update +} + +//------------------------------------------------------------------------------ +void vtkSlicerQWidgetWidget::SetRepresentation(vtkMRMLAbstractWidgetRepresentation* rep) +{ + this->Superclass::SetRepresentation(rep); + + vtkSlicerQWidgetRepresentation* qWidgetRep = vtkSlicerQWidgetRepresentation::SafeDownCast(rep); + if (!qWidgetRep) + { + vtkErrorMacro("SetRepresentation: Given representation is not a vtkSlicerQWidgetRepresentation"); + } +} + +//------------------------------------------------------------------------------ +void vtkSlicerQWidgetWidget::PrintSelf(ostream& os, vtkIndent indent) +{ + this->Superclass::PrintSelf(os, indent); +} diff --git a/GUIWidgets/VTKWidgets/vtkSlicerQWidgetWidget.h b/GUIWidgets/VTKWidgets/vtkSlicerQWidgetWidget.h new file mode 100644 index 0000000..304a15d --- /dev/null +++ b/GUIWidgets/VTKWidgets/vtkSlicerQWidgetWidget.h @@ -0,0 +1,103 @@ +/*============================================================================== + + Program: 3D Slicer + + Portions (c) Copyright Brigham and Women's Hospital (BWH) All Rights Reserved. + + See COPYRIGHT.txt + or http://www.slicer.org/copyright/copyright.txt for details. + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + This file was originally developed by Csaba Pinter, EBATINCA, S.L., and + development was supported by "ICEX Espana Exportacion e Inversiones" under + the program "Inversiones de Empresas Extranjeras en Actividades de I+D + (Fondo Tecnologico)- Convocatoria 2021" + +==============================================================================*/ + +/** + * @class vtkSlicerQWidgetWidget + * @brief 3D VTK widget for a QWidget + * + * This 3D widget handles events between VTK and Qt for a QWidget placed + * in a scene. It currently takes 6dof events as from VR controllers and + * if they intersect the widget it converts them to Qt events and fires + * them off. + */ + +#ifndef vtkSlicerQWidgetWidget_h +#define vtkSlicerQWidgetWidget_h + +#include "vtkSlicerGUIWidgetsModuleVTKWidgetsExport.h" + +#include "vtkSlicerMarkupsWidget.h" + +// Qt includes +#include + +class vtkSlicerQWidgetRepresentation; + +class VTK_SLICER_GUIWIDGETS_MODULE_VTKWIDGETS_EXPORT vtkSlicerQWidgetWidget : public vtkSlicerMarkupsWidget +{ + friend class vtkInteractionCallback; + +public: + /// Instantiate the object. + static vtkSlicerQWidgetWidget* New(); + + ///@{ + /// Standard vtkObject methods + vtkTypeMacro(vtkSlicerQWidgetWidget, vtkSlicerMarkupsWidget); + void PrintSelf(ostream& os, vtkIndent indent) override; + ///@} + + /// Create instance of the markups widget + vtkSlicerMarkupsWidget* CreateInstance() const override; + + /// Specify an instance of vtkSlicerQWidgetRepresentation used to represent this + /// widget in the scene. Note that the representation is a subclass of vtkProp + /// so it can be added to the renderer independent of the widget. + void SetRepresentation(vtkMRMLAbstractWidgetRepresentation *r) override; + + // Description: + // Disable/Enable the widget if needed. + // Unobserved the camera if the widget is disabled. + //void SetEnabled(int enabling) override; + + /// Return the representation as a vtkSlicerQWidgetRepresentation + vtkSlicerQWidgetRepresentation* GetQWidgetRepresentation(); + + /// Create the default widget representation and initializes the widget and representation. + void CreateDefaultRepresentation( + vtkMRMLMarkupsDisplayNode* markupsDisplayNode, vtkMRMLAbstractViewNode* viewNode, vtkRenderer* renderer) override; + +protected: + vtkSlicerQWidgetWidget(); + ~vtkSlicerQWidgetWidget() override; + /* + // Manage the state of the widget + int WidgetState; + enum _WidgetState + { + Start = 0, + Active + }; + */ + QPointF LastWidgetCoordinates; + + // These methods handle events + //static void SelectAction3D(vtkAbstractWidget*); + //static void EndSelectAction3D(vtkAbstractWidget*); + //static void MoveAction3D(vtkAbstractWidget*); + +private: + vtkSlicerQWidgetWidget(const vtkSlicerQWidgetWidget&) = delete; + void operator=(const vtkSlicerQWidgetWidget&) = delete; +}; + +#endif diff --git a/GUIWidgets/Widgets/CMakeLists.txt b/GUIWidgets/Widgets/CMakeLists.txt new file mode 100644 index 0000000..8b95ade --- /dev/null +++ b/GUIWidgets/Widgets/CMakeLists.txt @@ -0,0 +1,38 @@ +project(qSlicer${MODULE_NAME}ModuleWidgets) + +set(KIT ${PROJECT_NAME}) + +set(${KIT}_EXPORT_DIRECTIVE "Q_SLICER_MODULE_${MODULE_NAME_UPPER}_WIDGETS_EXPORT") + +set(${KIT}_INCLUDE_DIRECTORIES + ) + +set(${KIT}_SRCS + ) + +set(${KIT}_MOC_SRCS + ) + +set(${KIT}_UI_SRCS + ) + +set(${KIT}_RESOURCES + ../Resources/qSlicer${MODULE_NAME}Module.qrc + ) + +set(${KIT}_TARGET_LIBRARIES + vtkSlicer${MODULE_NAME}ModuleLogic + ) + +#----------------------------------------------------------------------------- +SlicerMacroBuildModuleWidgets( + NAME ${KIT} + EXPORT_DIRECTIVE ${${KIT}_EXPORT_DIRECTIVE} + INCLUDE_DIRECTORIES ${${KIT}_INCLUDE_DIRECTORIES} + SRCS ${${KIT}_SRCS} + MOC_SRCS ${${KIT}_MOC_SRCS} + UI_SRCS ${${KIT}_UI_SRCS} + TARGET_LIBRARIES ${${KIT}_TARGET_LIBRARIES} + RESOURCES ${${KIT}_RESOURCES} + WRAP_PYTHONQT + ) diff --git a/GUIWidgets/qSlicerGUIWidgetsModule.cxx b/GUIWidgets/qSlicerGUIWidgetsModule.cxx new file mode 100644 index 0000000..22def63 --- /dev/null +++ b/GUIWidgets/qSlicerGUIWidgetsModule.cxx @@ -0,0 +1,157 @@ +/*============================================================================== + + Program: 3D Slicer + + Portions (c) Copyright Brigham and Women's Hospital (BWH) All Rights Reserved. + + See COPYRIGHT.txt + or http://www.slicer.org/copyright/copyright.txt for details. + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + This file was originally developed by Csaba Pinter, EBATINCA, S.L., and + development was supported by "ICEX Espana Exportacion e Inversiones" under + the program "Inversiones de Empresas Extranjeras en Actividades de I+D + (Fondo Tecnologico)- Convocatoria 2021" + +==============================================================================*/ + +// GUIWidgets MRML includes +#include + +// GUIWidgets Logic includes +#include + +// GUIWidgets VTKWidgets includes +#include + +// GUIWidgets includes +#include "qSlicerGUIWidgetsModule.h" +#include "qSlicerGUIWidgetsModuleWidget.h" + +// Markups Logic includes +#include + +// Markups Widgets includes +#include "qMRMLMarkupsOptionsWidgetsFactory.h" + +// Qt includes +#include + +//----------------------------------------------------------------------------- +/// \ingroup Slicer_QtModules_ExtensionTemplate +class qSlicerGUIWidgetsModulePrivate +{ +public: + qSlicerGUIWidgetsModulePrivate(); +}; + +//----------------------------------------------------------------------------- +// qSlicerGUIWidgetsModulePrivate methods + +//----------------------------------------------------------------------------- +qSlicerGUIWidgetsModulePrivate::qSlicerGUIWidgetsModulePrivate() +{ +} + +//----------------------------------------------------------------------------- +// qSlicerGUIWidgetsModule methods + +//----------------------------------------------------------------------------- +qSlicerGUIWidgetsModule::qSlicerGUIWidgetsModule(QObject* _parent) + : Superclass(_parent) + , d_ptr(new qSlicerGUIWidgetsModulePrivate) +{ +} + +//----------------------------------------------------------------------------- +qSlicerGUIWidgetsModule::~qSlicerGUIWidgetsModule() +{ +} + +//----------------------------------------------------------------------------- +QString qSlicerGUIWidgetsModule::helpText() const +{ + return "This is a loadable module that can be bundled in an extension"; +} + +//----------------------------------------------------------------------------- +QString qSlicerGUIWidgetsModule::acknowledgementText() const +{ + return "This work was partially funded by the grant 'ICEX Espana Exportacion e Inversiones' under\ + the program 'Inversiones de Empresas Extranjeras en Actividades de I+D\ + (Fondo Tecnologico)- Convocatoria 2021'"; +} + +//----------------------------------------------------------------------------- +QStringList qSlicerGUIWidgetsModule::contributors() const +{ + QStringList moduleContributors; + moduleContributors << QString("Csaba Pinter (Ebatinca)"); + return moduleContributors; +} + +//----------------------------------------------------------------------------- +QIcon qSlicerGUIWidgetsModule::icon() const +{ + return QIcon(":/Icons/GUIWidgets.png"); +} + +//----------------------------------------------------------------------------- +QStringList qSlicerGUIWidgetsModule::categories() const +{ + return QStringList() << "Virtual Reality"; +} + +//----------------------------------------------------------------------------- +QStringList qSlicerGUIWidgetsModule::dependencies() const +{ + return QStringList() << "Markups"; +} + +//----------------------------------------------------------------------------- +void qSlicerGUIWidgetsModule::setup() +{ + this->Superclass::setup(); + + // Register displayable managers (same displayable manager handles both slice and 3D views) + //TODO: We do not register it because since the MRML node is a subclass of the plane markup, the markups DM takes over + //vtkMRMLThreeDViewDisplayableManagerFactory::GetInstance()->RegisterDisplayableManager("vtkMRMLGUIWidgetsDisplayableManager"); + + // Register markups + vtkSlicerApplicationLogic* appLogic = this->appLogic(); + if (!appLogic) + { + qCritical() << Q_FUNC_INFO << " : invalid application logic."; + return; + } + vtkSlicerMarkupsLogic* markupsLogic = vtkSlicerMarkupsLogic::SafeDownCast(appLogic->GetModuleLogic("Markups")); + if (!markupsLogic) + { + qCritical() << Q_FUNC_INFO << " : invalid markups logic."; + return; + } + vtkNew guiWidgetNode; + vtkNew vtkQWidgetWidget; + markupsLogic->RegisterMarkupsNode(guiWidgetNode, vtkQWidgetWidget); + + // Create and configure the additional widgets + //auto optionsWidgetFactory = qSlicerMarkupsAdditionalOptionsWidgetsFactory::instance(); + //optionsWidgetFactory->registerAdditionalOptionsWidget(new qSlicerMarkupsGUIWidget()); +} + +//----------------------------------------------------------------------------- +qSlicerAbstractModuleRepresentation* qSlicerGUIWidgetsModule::createWidgetRepresentation() +{ + return new qSlicerGUIWidgetsModuleWidget; +} + +//----------------------------------------------------------------------------- +vtkMRMLAbstractLogic* qSlicerGUIWidgetsModule::createLogic() +{ + return vtkSlicerGUIWidgetsLogic::New(); +} diff --git a/GUIWidgets/qSlicerGUIWidgetsModule.h b/GUIWidgets/qSlicerGUIWidgetsModule.h new file mode 100644 index 0000000..89edc63 --- /dev/null +++ b/GUIWidgets/qSlicerGUIWidgetsModule.h @@ -0,0 +1,77 @@ +/*============================================================================== + + Program: 3D Slicer + + Portions (c) Copyright Brigham and Women's Hospital (BWH) All Rights Reserved. + + See COPYRIGHT.txt + or http://www.slicer.org/copyright/copyright.txt for details. + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + This file was originally developed by Csaba Pinter, EBATINCA, S.L., and + development was supported by "ICEX Espana Exportacion e Inversiones" under + the program "Inversiones de Empresas Extranjeras en Actividades de I+D + (Fondo Tecnologico)- Convocatoria 2021" + +==============================================================================*/ + +#ifndef __qSlicerGUIWidgetsModule_h +#define __qSlicerGUIWidgetsModule_h + +// Slicer includes +#include "qSlicerLoadableModule.h" + +#include "qSlicerGUIWidgetsModuleExport.h" + +class qSlicerGUIWidgetsModulePrivate; + +/// \ingroup Slicer_QtModules_ExtensionTemplate +class Q_SLICER_QTMODULES_GUIWIDGETS_EXPORT qSlicerGUIWidgetsModule : public qSlicerLoadableModule +{ + Q_OBJECT + Q_PLUGIN_METADATA(IID "org.slicer.modules.loadable.qSlicerLoadableModule/1.0"); + Q_INTERFACES(qSlicerLoadableModule); + +public: + + typedef qSlicerLoadableModule Superclass; + explicit qSlicerGUIWidgetsModule(QObject *parent=0); + virtual ~qSlicerGUIWidgetsModule(); + + qSlicerGetTitleMacro(QTMODULE_TITLE); + + virtual QString helpText()const; + virtual QString acknowledgementText()const; + virtual QStringList contributors()const; + + virtual QIcon icon()const; + + virtual QStringList categories()const; + virtual QStringList dependencies() const; + +protected: + + /// Initialize the module. Register the volumes reader/writer + virtual void setup(); + + /// Create and return the widget representation associated to this module + virtual qSlicerAbstractModuleRepresentation* createWidgetRepresentation(); + + /// Create and return the logic associated to this module + virtual vtkMRMLAbstractLogic* createLogic(); + +protected: + QScopedPointer d_ptr; + +private: + Q_DECLARE_PRIVATE(qSlicerGUIWidgetsModule); + Q_DISABLE_COPY(qSlicerGUIWidgetsModule); + +}; + +#endif diff --git a/GUIWidgets/qSlicerGUIWidgetsModuleWidget.cxx b/GUIWidgets/qSlicerGUIWidgetsModuleWidget.cxx new file mode 100644 index 0000000..c07a6ca --- /dev/null +++ b/GUIWidgets/qSlicerGUIWidgetsModuleWidget.cxx @@ -0,0 +1,403 @@ +/*============================================================================== + + Program: 3D Slicer + + Portions (c) Copyright Brigham and Women's Hospital (BWH) All Rights Reserved. + + See COPYRIGHT.txt + or http://www.slicer.org/copyright/copyright.txt for details. + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + This file was originally developed by Csaba Pinter, EBATINCA, S.L., and + development was supported by "ICEX Espana Exportacion e Inversiones" under + the program "Inversiones de Empresas Extranjeras en Actividades de I+D + (Fondo Tecnologico)- Convocatoria 2021" + +==============================================================================*/ + +// GUI Widgets includes +#include "qSlicerGUIWidgetsModuleWidget.h" +#include "ui_qSlicerGUIWidgetsModuleWidget.h" + +#include "vtkMRMLGUIWidgetNode.h" +#include "vtkMRMLGUIWidgetDisplayNode.h" + +#include "vtkSlicerQWidgetWidget.h" +#include "vtkSlicerQWidgetRepresentation.h" + +#include "vtkSlicerQWidgetTexture.h" + +// VirtualReality includes +#include "vtkMRMLVirtualRealityViewNode.h" +#include "vtkSlicerVirtualRealityLogic.h" + +// Slicer includes +#include "qSlicerApplication.h" +#include "qSlicerLayoutManager.h" + +#include "vtkSlicerApplicationLogic.h" + +// qMRMLWidget includes +#include "qMRMLThreeDView.h" +#include "qMRMLThreeDWidget.h" + +// Markups Logic includes +#include + +// Virtual Reality includes +#include "qMRMLVirtualRealityHomeWidget.h" +#include "qMRMLVirtualRealityDataModuleWidget.h" +#include "qMRMLVirtualRealitySegmentEditorWidget.h" +#include "qMRMLVirtualRealityTransformWidget.h" + +// MRML includes +#include "vtkMRMLLinearTransformNode.h" +#include "vtkMRMLScene.h" +#include "vtkMRMLViewNode.h" + +#include "vtkMRMLMarkupsDisplayableManager.h" +#include "vtkMRMLCameraDisplayableManager.h" +#include "vtkMRMLMarkupsDisplayableManagerHelper.h" +#include "vtkMRMLVirtualRealityViewDisplayableManagerFactory.h" + +// VTK includes +#include "vtkRenderer.h" +#include "vtkMatrix4x4.h" +#include "vtkPlaneSource.h" +#include "vtkPolyData.h" +#include "vtkPolyDataMapper.h" +#include "vtkDataSet.h" +#include "vtkCellLocator.h" + +// Qt includes +#include +#include +#include +#include +#include +#include +#include +#include + +//----------------------------------------------------------------------------- +/// \ingroup Slicer_QtModules_ExtensionTemplate +class qSlicerGUIWidgetsModuleWidgetPrivate: public Ui_qSlicerGUIWidgetsModuleWidget +{ +public: + qSlicerGUIWidgetsModuleWidgetPrivate(); +}; + +//----------------------------------------------------------------------------- +// qSlicerGUIWidgetsModuleWidgetPrivate methods + +//----------------------------------------------------------------------------- +qSlicerGUIWidgetsModuleWidgetPrivate::qSlicerGUIWidgetsModuleWidgetPrivate() +{ +} + + +//----------------------------------------------------------------------------- +// qSlicerGUIWidgetsModuleWidget methods + +//----------------------------------------------------------------------------- +qSlicerGUIWidgetsModuleWidget::qSlicerGUIWidgetsModuleWidget(QWidget* _parent) + : Superclass( _parent ) + , d_ptr( new qSlicerGUIWidgetsModuleWidgetPrivate ) +{ +} + +//----------------------------------------------------------------------------- +qSlicerGUIWidgetsModuleWidget::~qSlicerGUIWidgetsModuleWidget() +{ + this->GUIWidgetsMap.clear(); +} + +//----------------------------------------------------------------------------- +void qSlicerGUIWidgetsModuleWidget::setup() +{ + Q_D(qSlicerGUIWidgetsModuleWidget); + d->setupUi(this); + this->Superclass::setup(); + + QObject::connect(d->AddHelloWorldGUIWidgetNodeButton, SIGNAL(clicked()), this, SLOT(onAddHelloWorldNodeClicked())); + QObject::connect(d->UpdateButtonLabelButton, SIGNAL(clicked()), this, SLOT(onUpdateButtonLabelButtonClicked())); + + QObject::connect(d->AddHomeWidgetButton, SIGNAL(clicked()), this, SLOT(onAddHomeWidgetButtonClicked())); + QObject::connect(d->AddDataModuleWidgetButton, SIGNAL(clicked()), this, SLOT(onAddDataModuleWidgetButtonClicked())); + QObject::connect(d->AddSegmentEditorWidgetButton, SIGNAL(clicked()), this, SLOT(onAddSegmentEditorWidgetButtonClicked())); + QObject::connect(d->AddTransformWidgetButton, SIGNAL(clicked()), this, SLOT(onAddTransformWidgetButtonClicked())); + + QObject::connect(d->SetUpInteractionButton, SIGNAL(clicked()), this, SLOT(onSetUpInteractionButtonClicked())); + QObject::connect(d->StartInteractionButton, SIGNAL(clicked()), this, SLOT(onStartInteractionButtonClicked())); +} + +//----------------------------------------------------------------------------- +void qSlicerGUIWidgetsModuleWidget::setWidgetToGUIWidgetMarkupsNode(vtkMRMLGUIWidgetNode* node, QWidget* widget) +{ + if (!node) + { + return; + } + + node->SetWidget((void*)widget); + + this->GUIWidgetsMap[node] = widget; +} + +//----------------------------------------------------------------------------- +QWidget* qSlicerGUIWidgetsModuleWidget::onAddHelloWorldNodeClicked() +{ + qSlicerApplication* app = qSlicerApplication::application(); + vtkMRMLGUIWidgetNode* widgetNode = vtkMRMLGUIWidgetNode::SafeDownCast( + app->mrmlScene()->AddNewNodeByClass("vtkMRMLGUIWidgetNode") ); + widgetNode->SetName("TestButtonWidgetNode"); + + QPushButton* newButton = new QPushButton("Hello world!"); + this->setWidgetToGUIWidgetMarkupsNode(widgetNode, newButton); + + return newButton; +} + +//----------------------------------------------------------------------------- +void qSlicerGUIWidgetsModuleWidget::onUpdateButtonLabelButtonClicked() +{ + Q_D(qSlicerGUIWidgetsModuleWidget); + + // Get last widget + QWidget* lastWidget = this->GUIWidgetsMap.last(); + + QPushButton* button = qobject_cast(lastWidget); + if (button) + { + button->setText(d->NewLabelLineEdit->text()); + } + else + { + qCritical() << Q_FUNC_INFO << ": Widget is not a push button"; + } +} + +//----------------------------------------------------------------------------- +void qSlicerGUIWidgetsModuleWidget::onAddHomeWidgetButtonClicked() +{ + Q_D(qSlicerGUIWidgetsModuleWidget); + + qSlicerApplication* app = qSlicerApplication::application(); + vtkMRMLGUIWidgetNode* widgetNode = vtkMRMLGUIWidgetNode::SafeDownCast( + app->mrmlScene()->AddNewNodeByClass("vtkMRMLGUIWidgetNode") ); + widgetNode->SetName("HomeWidgetNode"); + + qMRMLVirtualRealityHomeWidget* widget = new qMRMLVirtualRealityHomeWidget(); + widget->setMRMLScene(app->mrmlScene()); + this->setWidgetToGUIWidgetMarkupsNode(widgetNode, widget); +} + +//----------------------------------------------------------------------------- +void qSlicerGUIWidgetsModuleWidget::onAddDataModuleWidgetButtonClicked() +{ + Q_D(qSlicerGUIWidgetsModuleWidget); + + qSlicerApplication* app = qSlicerApplication::application(); + vtkMRMLGUIWidgetNode* widgetNode = vtkMRMLGUIWidgetNode::SafeDownCast( + app->mrmlScene()->AddNewNodeByClass("vtkMRMLGUIWidgetNode") ); + widgetNode->SetName("DataModuleWidgetNode"); + + qMRMLVirtualRealityDataModuleWidget* widget = new qMRMLVirtualRealityDataModuleWidget(); + widget->setMRMLScene(app->mrmlScene()); + this->setWidgetToGUIWidgetMarkupsNode(widgetNode, widget); +} + +//----------------------------------------------------------------------------- +void qSlicerGUIWidgetsModuleWidget::onAddSegmentEditorWidgetButtonClicked() +{ + Q_D(qSlicerGUIWidgetsModuleWidget); + + qSlicerApplication* app = qSlicerApplication::application(); + vtkMRMLGUIWidgetNode* widgetNode = vtkMRMLGUIWidgetNode::SafeDownCast( + app->mrmlScene()->AddNewNodeByClass("vtkMRMLGUIWidgetNode") ); + widgetNode->SetName("SegmentEditorWidgetNode"); + + qMRMLVirtualRealitySegmentEditorWidget* widget = new qMRMLVirtualRealitySegmentEditorWidget(); + widget->setMRMLScene(app->mrmlScene()); + this->setWidgetToGUIWidgetMarkupsNode(widgetNode, widget); +} + +//----------------------------------------------------------------------------- +void qSlicerGUIWidgetsModuleWidget::onAddTransformWidgetButtonClicked() +{ + Q_D(qSlicerGUIWidgetsModuleWidget); + + qSlicerApplication* app = qSlicerApplication::application(); + vtkMRMLGUIWidgetNode* widgetNode = vtkMRMLGUIWidgetNode::SafeDownCast( + app->mrmlScene()->AddNewNodeByClass("vtkMRMLGUIWidgetNode") ); + widgetNode->SetName("TransformWidgetNode"); + + vtkSlicerVirtualRealityLogic* vrLogic = + vtkSlicerVirtualRealityLogic::SafeDownCast(app->applicationLogic()->GetModuleLogic("VirtualReality")); + if (!vrLogic) + { + qCritical() << Q_FUNC_INFO << " : invalid VR logic"; + return; + } + + qMRMLVirtualRealityTransformWidget* widget = new qMRMLVirtualRealityTransformWidget(vrLogic->GetVirtualRealityViewNode()); + widget->setMRMLScene(app->mrmlScene()); + this->setWidgetToGUIWidgetMarkupsNode(widgetNode, widget); +} + +//----------------------------------------------------------------------------- +void qSlicerGUIWidgetsModuleWidget::onSetUpInteractionButtonClicked() +{ + std::cout << "----- onSetUpInteractionButtonClicked ----- \n"; +} + +//----------------------------------------------------------------------------- +void qSlicerGUIWidgetsModuleWidget::onStartInteractionButtonClicked() +{ + std::cout << "\n\nqSlicerGUIWidgetsModuleWidget::onStartInteractionButtonClicked() \n"; + + // Pointer transform + qSlicerApplication* app = qSlicerApplication::application(); + vtkMRMLLinearTransformNode* transformNode = vtkMRMLLinearTransformNode::SafeDownCast(app->mrmlScene()->GetFirstNodeByName("PointerTransform")); + if (!transformNode) + { + std::cout << "ERROR: Pointer transform was not found in scene... \n"; + return; + } + + // Define maximum distance for interaction + double maxDistanceForInteraction = 2000; // mm + + // Line points + double pointA_h[4] = { 0.0, 0.0, 0.0, 1.0 }; + double pointB_h[4] = { 0.0, 0.0, -maxDistanceForInteraction, 1.0 }; + + // Get transformed line points + double pointA_transf_h[4] = { 0.0, 0.0, 0.0, 1.0 }; + double pointB_transf_h[4] = { 0.0, 0.0, 0.0, 1.0 }; + vtkNew matrixTransformToWorld; + transformNode->GetMatrixTransformToWorld(matrixTransformToWorld); + matrixTransformToWorld->MultiplyPoint(pointA_h, pointA_transf_h); + matrixTransformToWorld->MultiplyPoint(pointB_h, pointB_transf_h); + double pointA_transf[3] = { pointA_transf_h[0], pointA_transf_h[1], pointA_transf_h[2] }; + double pointB_transf[3] = { pointB_transf_h[0], pointB_transf_h[1], pointB_transf_h[2] }; + + // Get GUI widget + vtkMRMLGUIWidgetNode* widgetNode = vtkMRMLGUIWidgetNode::SafeDownCast(app->mrmlScene()->GetFirstNodeByName("HomeWidgetNode")); + + // Get displayable manager + qSlicerLayoutManager* layoutManager = qSlicerApplication::application()->layoutManager(); + if (!layoutManager) + { + // application is closing + return; + } + qMRMLThreeDWidget* threeDWidget = layoutManager->threeDWidget(0); + vtkMRMLMarkupsDisplayableManager* markupsDisplayableManager = vtkMRMLMarkupsDisplayableManager::SafeDownCast(threeDWidget->threeDView()->displayableManagerByClassName("vtkMRMLMarkupsDisplayableManager")); + + // Get widget representation from displayabale manager + vtkMRMLMarkupsDisplayableManagerHelper* helper = markupsDisplayableManager->GetHelper(); + vtkSlicerQWidgetWidget* widget = vtkSlicerQWidgetWidget::SafeDownCast(helper->GetWidget(widgetNode->GetMarkupsDisplayNode())); + vtkSlicerQWidgetRepresentation* rep = vtkSlicerQWidgetRepresentation::SafeDownCast(widget->GetRepresentation()); + + // Get plane source + vtkPlaneSource* planeSource = vtkPlaneSource::SafeDownCast(rep->GetPlaneSource()); + + // Get plane normal + double* planeNormal = planeSource->GetNormal(); + std::cout << "Plane normal: [" << planeNormal[0] << ", " << planeNormal[1] << ", " << planeNormal[2] << "] \n"; + + // Get plane reference points + double* planePointSW = planeSource->GetOrigin(); // bottom left corner + double* planePointSE = planeSource->GetPoint1(); // bottom right corner + double* planePointNW = planeSource->GetPoint2(); // top left corner + double translationWtoE[3] = {0.0, 0.0, 0.0}; + vtkMath::Subtract(planePointSE, planePointSW, translationWtoE); + double planePointNE[3] = { 0.0, 0.0, 0.0 }; + vtkMath::Add(planePointNW, translationWtoE, planePointNE); + std::cout << "Plane point NW: [" << planePointNW[0] << ", " << planePointNW[1] << ", " << planePointNW[2] << "] \n"; + std::cout << "Plane point NE: [" << planePointNE[0] << ", " << planePointNE[1] << ", " << planePointNE[2] << "] \n"; + std::cout << "Plane point SW: [" << planePointSW[0] << ", " << planePointSW[1] << ", " << planePointSW[2] << "] \n"; + std::cout << "Plane point SE: [" << planePointSE[0] << ", " << planePointSE[1] << ", " << planePointSE[2] << "] \n"; + + // Compute intersection point + vtkNew cellLocator; + cellLocator->SetDataSet(planeSource->GetOutput()); + cellLocator->BuildLocator(); + double tolerance = 0.001; + double t = 0.0; + double pcoords[3] = { 0.0 }; + int subId = 0; + vtkIdType cellId = 0; + vtkNew cell; + double intersectionPoint[3]= { 0.0, 0.0, 0.0 }; + int foundIntersection = cellLocator->IntersectWithLine(pointA_transf, pointB_transf, tolerance, t, intersectionPoint, pcoords, subId, cellId, cell); + if (foundIntersection) + { + std::cout << "Intersection point: [" << intersectionPoint[0] << ", " << intersectionPoint[1] << ", " << intersectionPoint[2] << "] \n"; + } + else + { + std::cout << "No intersection was found... \n"; + return; + } + + // Get plane dimensions + vtkSlicerQWidgetTexture* texture = rep->GetQWidgetTexture(); + QWidget* qWidget = texture->GetWidget(); + if (!qWidget) + { + return; + } + QRect rect = qWidget->geometry(); + if (rect.width() < 2 || rect.height() < 2) + { + return; + } + std::cout << "Widget dimensions: width = " << rect.width() << " and height = " << rect.height() << "\n"; + double spacingMmPerPixel = rep->GetSpacingMmPerPixel(); + double bounds[6] = { + -(double)(rect.width() / 2) * spacingMmPerPixel, (double)rect.width() / 2 * spacingMmPerPixel, + -0.5, 0.5, + -(double)(rect.height() / 2) * spacingMmPerPixel, (double)rect.height() / 2 * spacingMmPerPixel + }; + std::cout << "Widget bounds: [ " << bounds[0] << ", " << bounds[1] << ", " << bounds[2] << ", " << bounds[3] << ", " << bounds[4] << ", " << bounds[5] << "\n"; + + // Compute pixel position + double intersectionPointVector[3] = { intersectionPoint[0] - planePointNW[0], intersectionPoint[1] - planePointNW[1], intersectionPoint[2] - planePointNW[2] }; + double xPlaneAxis[3] = { planePointNE[0] - planePointNW[0], planePointNE[1] - planePointNW[1], planePointNE[2] - planePointNW[2] }; + double yPlaneAxis[3] = { planePointSW[0] - planePointNW[0], planePointSW[1] - planePointNW[1], planePointSW[2] - planePointNW[2] }; + vtkMath::MultiplyScalar(xPlaneAxis, vtkMath::Dot(intersectionPointVector, xPlaneAxis) / vtkMath::Dot(xPlaneAxis, xPlaneAxis)); + vtkMath::MultiplyScalar(yPlaneAxis, vtkMath::Dot(intersectionPointVector, yPlaneAxis) / vtkMath::Dot(yPlaneAxis, yPlaneAxis)); + double xIntersectionPoint[3] = { 0.0, 0.0, 0.0 }; + double yIntersectionPoint[3] = { 0.0, 0.0, 0.0 }; + vtkMath::Add(planePointNW, xPlaneAxis, xIntersectionPoint); + vtkMath::Add(planePointNW, yPlaneAxis, yIntersectionPoint); + vtkMath::Subtract(xIntersectionPoint, planePointNW, xIntersectionPoint); // subtract plane origin + vtkMath::Subtract(yIntersectionPoint, planePointNW, yIntersectionPoint); // subtract plane origin + double xPositionMm = vtkMath::Norm(xIntersectionPoint); + double yPositionMm = vtkMath::Norm(yIntersectionPoint); + std::cout << "Pointer intersection position (mm): [ " << xPositionMm << ", " << yPositionMm << "] \n"; + int xPositionPixels = xPositionMm / spacingMmPerPixel; + int yPositionPixels = yPositionMm / spacingMmPerPixel; + std::cout << "Pointer intersection position (pixels): [ " << xPositionPixels << ", " << yPositionPixels << "] \n"; + + // Send press event + QGraphicsSceneMouseEvent pressEvent(QEvent::GraphicsSceneMousePress); + pressEvent.setScenePos(QPointF(xPositionPixels, yPositionPixels)); + pressEvent.setButton(Qt::LeftButton); + pressEvent.setButtons(Qt::LeftButton); + QApplication::sendEvent(texture->GetScene(), &pressEvent); + + // Send release event + QGraphicsSceneMouseEvent releaseEvent(QEvent::GraphicsSceneMouseRelease); + releaseEvent.setScenePos(QPointF(xPositionPixels, yPositionPixels)); + releaseEvent.setButton(Qt::LeftButton); + releaseEvent.setButtons(Qt::LeftButton); + QApplication::sendEvent(texture->GetScene(), &releaseEvent); +} diff --git a/GUIWidgets/qSlicerGUIWidgetsModuleWidget.h b/GUIWidgets/qSlicerGUIWidgetsModuleWidget.h new file mode 100644 index 0000000..e58bd69 --- /dev/null +++ b/GUIWidgets/qSlicerGUIWidgetsModuleWidget.h @@ -0,0 +1,76 @@ +/*============================================================================== + + Program: 3D Slicer + + Portions (c) Copyright Brigham and Women's Hospital (BWH) All Rights Reserved. + + See COPYRIGHT.txt + or http://www.slicer.org/copyright/copyright.txt for details. + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + This file was originally developed by Csaba Pinter, EBATINCA, S.L., and + development was supported by "ICEX Espana Exportacion e Inversiones" under + the program "Inversiones de Empresas Extranjeras en Actividades de I+D + (Fondo Tecnologico)- Convocatoria 2021" + +==============================================================================*/ + +#ifndef __qSlicerGUIWidgetsModuleWidget_h +#define __qSlicerGUIWidgetsModuleWidget_h + +// Slicer includes +#include "qSlicerAbstractModuleWidget.h" + +#include "qSlicerGUIWidgetsModuleExport.h" + +// Qt includes +#include + +class qSlicerGUIWidgetsModuleWidgetPrivate; +class vtkMRMLGUIWidgetNode; + +/// \ingroup Slicer_QtModules_ExtensionTemplate +class Q_SLICER_QTMODULES_GUIWIDGETS_EXPORT qSlicerGUIWidgetsModuleWidget : + public qSlicerAbstractModuleWidget +{ + Q_OBJECT + +public: + + typedef qSlicerAbstractModuleWidget Superclass; + qSlicerGUIWidgetsModuleWidget(QWidget *parent=0); + virtual ~qSlicerGUIWidgetsModuleWidget(); + +public slots: + QWidget* onAddHelloWorldNodeClicked(); + void onUpdateButtonLabelButtonClicked(); + + void onAddHomeWidgetButtonClicked(); + void onAddDataModuleWidgetButtonClicked(); + void onAddSegmentEditorWidgetButtonClicked(); + void onAddTransformWidgetButtonClicked(); + void onSetUpInteractionButtonClicked(); + void onStartInteractionButtonClicked(); + + /// Assign widget to a GUIWidget markups node + void setWidgetToGUIWidgetMarkupsNode(vtkMRMLGUIWidgetNode* node, QWidget* widget); + +protected: + QScopedPointer d_ptr; + + virtual void setup(); + +protected: + QMap GUIWidgetsMap; + +private: + Q_DECLARE_PRIVATE(qSlicerGUIWidgetsModuleWidget); + Q_DISABLE_COPY(qSlicerGUIWidgetsModuleWidget); +}; + +#endif diff --git a/PointerSimulator/CMakeLists.txt b/PointerSimulator/CMakeLists.txt new file mode 100644 index 0000000..36081e9 --- /dev/null +++ b/PointerSimulator/CMakeLists.txt @@ -0,0 +1,31 @@ +#----------------------------------------------------------------------------- +set(MODULE_NAME PointerSimulator) + +#----------------------------------------------------------------------------- +set(MODULE_PYTHON_SCRIPTS + ${MODULE_NAME}.py + ) + +set(MODULE_PYTHON_RESOURCES + Resources/Icons/${MODULE_NAME}.png + Resources/UI/${MODULE_NAME}.ui + ) + +#----------------------------------------------------------------------------- +slicerMacroBuildScriptedModule( + NAME ${MODULE_NAME} + SCRIPTS ${MODULE_PYTHON_SCRIPTS} + RESOURCES ${MODULE_PYTHON_RESOURCES} + WITH_GENERIC_TESTS + ) + +#----------------------------------------------------------------------------- +if(BUILD_TESTING) + + # Register the unittest subclass in the main script as a ctest. + # Note that the test will also be available at runtime. + slicer_add_python_unittest(SCRIPT ${MODULE_NAME}.py) + + # Additional build-time testing + add_subdirectory(Testing) +endif() diff --git a/PointerSimulator/PointerSimulator.py b/PointerSimulator/PointerSimulator.py new file mode 100644 index 0000000..a272176 --- /dev/null +++ b/PointerSimulator/PointerSimulator.py @@ -0,0 +1,367 @@ +import os +import unittest +import logging +import vtk, qt, ctk, slicer +from slicer.ScriptedLoadableModule import * +from slicer.util import VTKObservationMixin +import numpy as np +import time + +#------------------------------------------------------------------------------ +# +# PointerSimulator +# +#------------------------------------------------------------------------------ +class PointerSimulator(ScriptedLoadableModule): + + def __init__(self, parent): + ScriptedLoadableModule.__init__(self, parent) + self.parent.title = "PointerSimulator" + self.parent.categories = ["Virtual Reality"] + self.parent.dependencies = [] + self.parent.contributors = ["David Garcia Mato (Ebatinca, S.L.)"] + self.parent.helpText = """ """ + self.parent.acknowledgementText = """ """ + +#------------------------------------------------------------------------------ +# +# PointerSimulatorWidget +# +#------------------------------------------------------------------------------ +class PointerSimulatorWidget(ScriptedLoadableModuleWidget, VTKObservationMixin): + + def __init__(self, parent): + """ + Called when the user opens the module the first time and the widget is initialized. + """ + ScriptedLoadableModuleWidget.__init__(self, parent) + VTKObservationMixin.__init__(self) + + self.logic = PointerSimulatorLogic(self) + self._parameterNode = None + self._updatingGUIFromParameterNode = False + + def setup(self): + """ + Called when the user opens the module the first time and the widget is initialized. + """ + ScriptedLoadableModuleWidget.setup(self) + + # Load widget from .ui file (created by Qt Designer). + # Additional widgets can be instantiated manually and added to self.layout. + uiWidget = slicer.util.loadUI(self.resourcePath('UI/PointerSimulator.ui')) + self.layout.addWidget(uiWidget) + self.ui = slicer.util.childWidgetVariables(uiWidget) + + # Set scene in MRML widgets. Make sure that in Qt designer the top-level qMRMLWidget's + # "mrmlSceneChanged(vtkMRMLScene*)" signal in is connected to each MRML widget's. + # "setMRMLScene(vtkMRMLScene*)" slot. + uiWidget.setMRMLScene(slicer.mrmlScene) + + # Connections + + # These connections ensure that we update parameter node when scene is closed + self.addObserver(slicer.mrmlScene, slicer.mrmlScene.StartCloseEvent, self.onSceneStartClose) + self.addObserver(slicer.mrmlScene, slicer.mrmlScene.EndCloseEvent, self.onSceneEndClose) + + # These connections ensure that whenever user changes some settings on the GUI, that is saved in the MRML scene + # (in the selected parameter node). + self.ui.setUpPointerButton.clicked.connect(self.onSetUpPointerButtonClicked) + + # Make sure parameter node is initialized (needed for module reload) + self.initializeParameterNode() + + def cleanup(self): + """ + Called when the application closes and the module widget is destroyed. + """ + self.removeObservers() + + def enter(self): + """ + Called each time the user opens this module. + """ + # Make sure parameter node exists and observed + self.initializeParameterNode() + + def exit(self): + """ + Called each time the user opens a different module. + """ + # Do not react to parameter node changes (GUI wlil be updated when the user enters into the module) + self.removeObserver(self._parameterNode, vtk.vtkCommand.ModifiedEvent, self.updateGUIFromParameterNode) + + def onSceneStartClose(self, caller, event): + """ + Called just before the scene is closed. + """ + # Parameter node will be reset, do not use it anymore + self.setParameterNode(None) + + def onSceneEndClose(self, caller, event): + """ + Called just after the scene is closed. + """ + # If this module is shown while the scene is closed then recreate a new parameter node immediately + if self.parent.isEntered: + self.initializeParameterNode() + + def initializeParameterNode(self): + """ + Ensure parameter node exists and observed. + """ + # Parameter node stores all user choices in parameter values, node selections, etc. + # so that when the scene is saved and reloaded, these settings are restored. + + self.setParameterNode(self.logic.getParameterNode()) + + # Select default input nodes if nothing is selected yet to save a few clicks for the user + if not self._parameterNode.GetNodeReference("InputVolume"): + firstVolumeNode = slicer.mrmlScene.GetFirstNodeByClass("vtkMRMLScalarVolumeNode") + if firstVolumeNode: + self._parameterNode.SetNodeReferenceID("InputVolume", firstVolumeNode.GetID()) + + def setParameterNode(self, inputParameterNode): + """ + Set and observe parameter node. + Observation is needed because when the parameter node is changed then the GUI must be updated immediately. + """ + + if inputParameterNode: + self.logic.setDefaultParameters(inputParameterNode) + + # Unobserve previously selected parameter node and add an observer to the newly selected. + # Changes of parameter node are observed so that whenever parameters are changed by a script or any other module + # those are reflected immediately in the GUI. + if self._parameterNode is not None: + self.removeObserver(self._parameterNode, vtk.vtkCommand.ModifiedEvent, self.updateGUIFromParameterNode) + self._parameterNode = inputParameterNode + if self._parameterNode is not None: + self.addObserver(self._parameterNode, vtk.vtkCommand.ModifiedEvent, self.updateGUIFromParameterNode) + + # Initial GUI update + self.updateGUIFromParameterNode() + + def updateGUIFromParameterNode(self, caller=None, event=None): + """ + This method is called whenever parameter node is changed. + The module GUI is updated to show the current state of the parameter node. + """ + + if self._parameterNode is None or self._updatingGUIFromParameterNode: + return + + # Make sure GUI changes do not call updateParameterNodeFromGUI (it could cause infinite loop) + self._updatingGUIFromParameterNode = True + + # All the GUI updates are done + self._updatingGUIFromParameterNode = False + + def updateParameterNodeFromGUI(self, caller=None, event=None): + """ + This method is called when the user makes any change in the GUI. + The changes are saved into the parameter node (so that they are restored when the scene is saved and loaded). + """ + + if self._parameterNode is None or self._updatingGUIFromParameterNode: + return + + wasModified = self._parameterNode.StartModify() # Modify all properties in a single batch + + self._parameterNode.EndModify(wasModified) + + def onSetUpPointerButtonClicked(self): + # Create pointer model + origin = [0.0, 0.0, 0.0] + direction = [0.0, 0.0, -1.0] + length = 500.0 + self.logic.updatePointerModel(origin, direction, length) + + # Create pointer transform + self.logic.updatePointerTransform(0.0,0.0,0.0) + + # Set pointer transform + self.ui.translationSliders.setMRMLTransformNode(self.logic.pointerTransform) + self.ui.rotationSliders.setMRMLTransformNode(self.logic.pointerTransform) + + # Transform pointer by controller transform + self.logic.applyControllerTransform() + +#------------------------------------------------------------------------------ +# +# PointerSimulatorLogic +# +#------------------------------------------------------------------------------ +class PointerSimulatorLogic(ScriptedLoadableModuleLogic, VTKObservationMixin): + + #------------------------------------------------------------------------------ + def __init__(self, widgetInstance, parent=None): + """ + Called when the logic class is instantiated. Can be used for initializing member variables. + """ + ScriptedLoadableModuleLogic.__init__(self, parent) + VTKObservationMixin.__init__(self) + + self.moduleWidget = widgetInstance + + # Pointer model + self.pointerModel = None + self.pointerLineSource = None + self.pointerRadius = 1.0 + + # Pointer transform + self.pointerTransform = None + + #------------------------------------------------------------------------------ + def setDefaultParameters(self, parameterNode): + """ + Initialize parameter node with default settings. + """ + if not parameterNode.GetParameter("TranslateRL"): + parameterNode.SetParameter("TranslateRL", "0.0") + if not parameterNode.GetParameter("TranslateAP"): + parameterNode.SetParameter("TranslateAP", "0.0") + if not parameterNode.GetParameter("TranslateSI"): + parameterNode.SetParameter("TranslateSI", "0.0") + + #------------------------------------------------------------------------------ + def updatePointerModel(self, origin, direction, length): + + # Get/create pointer model + if not self.pointerModel: + # Line source + self.pointerLineSource = vtk.vtkLineSource() + # Tube filter + tubeFilter = vtk.vtkTubeFilter() + tubeFilter.SetInputConnection(self.pointerLineSource.GetOutputPort()) + tubeFilter.SetRadius(self.pointerRadius) #Default is 0.5 + tubeFilter.SetNumberOfSides(50) + tubeFilter.Update() + # Create a mapper and actor + pointerMapper = vtk.vtkPolyDataMapper() + pointerMapper.SetInputConnection(tubeFilter.GetOutputPort()) + pointerActor = vtk.vtkActor() + pointerActor.SetMapper(pointerMapper) + # Create model node + self.pointerModel = slicer.vtkMRMLModelNode() + self.pointerModel.SetName('PointerModel') + self.pointerModel.SetPolyDataConnection(tubeFilter.GetOutputPort()) + slicer.mrmlScene.AddNode(self.pointerModel) + self.pointerModelDisplay = slicer.vtkMRMLModelDisplayNode() + self.pointerModelDisplay.SetSliceIntersectionVisibility(True) + self.pointerModelDisplay.SetColor(1,0,0) + self.pointerModelDisplay.SetOpacity(1.0) + slicer.mrmlScene.AddNode(self.pointerModelDisplay) + self.pointerModel.SetAndObserveDisplayNodeID(self.pointerModelDisplay.GetID()) + + # Disable toggle selectable property for model + self.pointerModel.SetSelectable(False) + + # Update pointer model properties + vtk.vtkMath().Normalize(direction) + startPoint = np.array(origin) + endPoint = startPoint + np.array(direction) * length + self.pointerLineSource.SetPoint1(startPoint) + self.pointerLineSource.SetPoint2(endPoint) + + # Apply scalars to simulate pointer fading away + self.createAndApplyColorTable(startPoint) + + #------------------------------------------------------------------------------ + def updatePointerTransform(self, translationRL, translationAP, translationSI): + + # Create pointer transform if it does not exist + if not self.pointerTransform: + self.pointerTransform=slicer.vtkMRMLLinearTransformNode() + self.pointerTransform.SetName("PointerTransform") + slicer.mrmlScene.AddNode(self.pointerTransform) + if self.pointerModel: + self.pointerModel.SetAndObserveTransformNodeID(self.pointerTransform.GetID()) + else: + logging.error('Pointer model was not found.') + + # Update transformation matrix + transform = vtk.vtkTransform() + transform.Translate(translationRL, translationAP, translationSI) + + # Set transform + self.pointerTransform.SetMatrixTransformToParent(transform.GetMatrix()) + + #------------------------------------------------------------------------------ + def createAndApplyColorTable(self, origin): + + # Get polydata + poly = self.pointerModel.GetPolyData() + + # Get number of points + numPoints = poly.GetNumberOfPoints() + + # Create scalar array + scalar_array = vtk.vtkFloatArray() + scalar_array.SetName("DistanceToOrigin") + scalar_array.SetNumberOfComponents(1) + + # Compute distance from each vertex to origin + normalizedDistanceToOrigin_array = list() + for i in range(numPoints): + point = poly.GetPoint(i) + distance = np.linalg.norm(np.array(point)-np.array(origin)) + normalizedDistanceToOrigin_array.append(distance) + + # Normalize distance from 0 to 1 + maxValue = max(normalizedDistanceToOrigin_array) + minValue = min(normalizedDistanceToOrigin_array) + for i in range(numPoints): + normalizedDistanceToOrigin_array[i] = (normalizedDistanceToOrigin_array[i] - minValue)/(maxValue - minValue) + normalizedDistanceToOrigin_array[i] = 1.0 - normalizedDistanceToOrigin_array[i] + + # Fill scalar array from list + scalar_array.SetNumberOfTuples(numPoints) + for j in range(numPoints): + scalar_array.SetTuple1(j,normalizedDistanceToOrigin_array[j]) + + # Add scalar to model + self.pointerModel.AddPointScalars(scalar_array) + + # Create custom color table + myColorTable = slicer.vtkMRMLColorTableNode() + myColorTable.SetTypeToUser() + myColorTable.SetNumberOfColors(256) + myColorTable.SetName("CustomColorTable") + for i in range(0,255): + myColorTable.SetColor(i, 1.0, 0.0, 0.0, (i+1e-16)/255.0) + + slicer.mrmlScene.AddNode(myColorTable) + + # Update visualization + self.pointerModel.GetDisplayNode().SetActiveScalarName(scalar_array.GetName()) + self.pointerModel.GetDisplayNode().SetScalarVisibility(True) + self.pointerModel.GetDisplayNode().SetAndObserveColorNodeID(myColorTable.GetID()) #apply color table + + #------------------------------------------------------------------------------ + def applyControllerTransform(self): + + # Get controller transform + try: + controllerTransform = slicer.util.getNode('VirtualReality.RightController') + except: + logging.error('ERROR: Right controller transform was not found in the scene.') + return + + # Apply transform + if self.pointerTransform: + self.pointerTransform.SetAndObserveTransformNodeID(controllerTransform.GetID()) + + + +#------------------------------------------------------------------------------ +# +# PointerSimulatorTest +# +#------------------------------------------------------------------------------ +class PointerSimulatorTest(ScriptedLoadableModuleTest): + + def runTest(self): + """Run as few or as many tests as needed here. + """ + pass \ No newline at end of file diff --git a/PointerSimulator/Resources/Icons/PointerSimulator.png b/PointerSimulator/Resources/Icons/PointerSimulator.png new file mode 100644 index 0000000..5d83ab4 Binary files /dev/null and b/PointerSimulator/Resources/Icons/PointerSimulator.png differ diff --git a/PointerSimulator/Resources/UI/PointerSimulator.ui b/PointerSimulator/Resources/UI/PointerSimulator.ui new file mode 100644 index 0000000..5c2b2b5 --- /dev/null +++ b/PointerSimulator/Resources/UI/PointerSimulator.ui @@ -0,0 +1,87 @@ + + + PointerSimulator + + + + 0 + 0 + 315 + 556 + + + + + + + Pointer Controller + + + + + + + + + Click + + + + + + Click + + + true + + + + + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + qMRMLTransformSliders + qMRMLWidget +
qMRMLTransformSliders.h
+
+ + qMRMLWidget + QWidget +
qMRMLWidget.h
+ 1 +
+ + ctkCollapsibleButton + QWidget +
ctkCollapsibleButton.h
+ 1 +
+ + ctkCollapsibleGroupBox + QGroupBox +
ctkCollapsibleGroupBox.h
+ 1 +
+
+ + +
diff --git a/PointerSimulator/Testing/CMakeLists.txt b/PointerSimulator/Testing/CMakeLists.txt new file mode 100644 index 0000000..655007a --- /dev/null +++ b/PointerSimulator/Testing/CMakeLists.txt @@ -0,0 +1 @@ +add_subdirectory(Python) diff --git a/PointerSimulator/Testing/Python/CMakeLists.txt b/PointerSimulator/Testing/Python/CMakeLists.txt new file mode 100644 index 0000000..5658d8b --- /dev/null +++ b/PointerSimulator/Testing/Python/CMakeLists.txt @@ -0,0 +1,2 @@ + +#slicer_add_python_unittest(SCRIPT ${MODULE_NAME}ModuleTest.py) diff --git a/PointerSimulator/__pycache__/PointerSimulator.cpython-36.pyc b/PointerSimulator/__pycache__/PointerSimulator.cpython-36.pyc new file mode 100644 index 0000000..576494d Binary files /dev/null and b/PointerSimulator/__pycache__/PointerSimulator.cpython-36.pyc differ diff --git a/VirtualReality/Logic/CMakeLists.txt b/VirtualReality/Logic/CMakeLists.txt index 19c8b53..b08f0d2 100644 --- a/VirtualReality/Logic/CMakeLists.txt +++ b/VirtualReality/Logic/CMakeLists.txt @@ -4,7 +4,7 @@ set(KIT ${PROJECT_NAME}) set(${KIT}_EXPORT_DIRECTIVE "VTK_SLICER_${MODULE_NAME_UPPER}_MODULE_LOGIC_EXPORT") -set(${KIT}_INCLUDE_DIRECTORIES +set(${KIT}_INCLUDE_DIRS ) set(${KIT}_SRCS diff --git a/VirtualReality/MRML/CMakeLists.txt b/VirtualReality/MRML/CMakeLists.txt index 112612f..cafd9b6 100644 --- a/VirtualReality/MRML/CMakeLists.txt +++ b/VirtualReality/MRML/CMakeLists.txt @@ -5,7 +5,7 @@ set(KIT ${PROJECT_NAME}) #----------------------------------------------------------------------------- set(${KIT}_EXPORT_DIRECTIVE "VTK_SLICER_${MODULE_NAME_UPPER}_MODULE_MRML_EXPORT") -set(${KIT}_INCLUDE_DIRECTORIES +set(${KIT}_INCLUDE_DIRS ${VTK_INCLUDE_DIRS} ) diff --git a/VirtualReality/Resources/menuTextureImage2.png b/VirtualReality/Resources/menuTextureImage2.png new file mode 100644 index 0000000..9cf74e2 Binary files /dev/null and b/VirtualReality/Resources/menuTextureImage2.png differ diff --git a/VirtualReality/SubjectHierarchyPlugins/CMakeLists.txt b/VirtualReality/SubjectHierarchyPlugins/CMakeLists.txt index 7427d03..7c3896e 100644 --- a/VirtualReality/SubjectHierarchyPlugins/CMakeLists.txt +++ b/VirtualReality/SubjectHierarchyPlugins/CMakeLists.txt @@ -4,7 +4,7 @@ set(KIT ${PROJECT_NAME}) set(${KIT}_EXPORT_DIRECTIVE "Q_SLICER_${MODULE_NAME_UPPER}_SUBJECT_HIERARCHY_PLUGINS_EXPORT") -set(${KIT}_INCLUDE_DIRECTORIES +set(${KIT}_INCLUDE_DIRS ${qSlicerSubjectHierarchyModuleWidgets_INCLUDE_DIRS} ${vtkSlicerSubjectHierarchyModuleLogic_INCLUDE_DIRS} ${qMRMLWidgets_INCLUDE_DIRS} diff --git a/VirtualReality/Widgets/CMakeLists.txt b/VirtualReality/Widgets/CMakeLists.txt index da8dd9b..e7d59b8 100644 --- a/VirtualReality/Widgets/CMakeLists.txt +++ b/VirtualReality/Widgets/CMakeLists.txt @@ -5,9 +5,12 @@ set(KIT ${PROJECT_NAME}) #----------------------------------------------------------------------------- set(${KIT}_EXPORT_DIRECTIVE "Q_SLICER_QTMODULES_${MODULE_NAME_UPPER}_WIDGETS_EXPORT") -set(${KIT}_INCLUDE_DIRECTORIES +set(${KIT}_INCLUDE_DIRS ${vtkSlicerVirtualRealityModuleMRML_INCLUDE_DIRS} ${VTK_INCLUDE_DIRS} + ${qSlicerSubjectHierarchyModuleWidgets_INCLUDE_DIRS} + ${qSlicerSegmentationsModuleWidgets_INCLUDE_DIRS} + ${qSlicerSegmentationsEditorEffects_INCLUDE_DIRS} ) set(${KIT}_SRCS @@ -16,19 +19,32 @@ set(${KIT}_SRCS qMRML${MODULE_NAME}View.h qMRML${MODULE_NAME}TransformWidget.cxx qMRML${MODULE_NAME}TransformWidget.h + qMRMLVirtualRealityHomeWidget.cxx + qMRMLVirtualRealityHomeWidget.h + qMRMLVirtualRealityDataModuleWidget.cxx + qMRMLVirtualRealityDataModuleWidget.h + qMRMLVirtualRealitySegmentEditorWidget.cxx + qMRMLVirtualRealitySegmentEditorWidget.h ) set(${KIT}_MOC_SRCS qMRML${MODULE_NAME}View.h qMRML${MODULE_NAME}TransformWidget.h qMRML${MODULE_NAME}View_p.h + qMRMLVirtualRealityHomeWidget.h + qMRMLVirtualRealityDataModuleWidget.h + qMRMLVirtualRealitySegmentEditorWidget.h ) set(${KIT}_UI_SRCS + Resources/UI/qMRMLVirtualRealityHomeWidget.ui + Resources/UI/qMRMLVirtualRealityDataModuleWidget.ui + Resources/UI/qMRMLVirtualRealitySegmentEditorWidget.ui Resources/UI/qMRML${MODULE_NAME}TransformWidget.ui ) set(${KIT}_RESOURCES + Resources/${KIT}.qrc Resources/qMRML${MODULE_NAME}TransformWidget.qrc ) @@ -36,6 +52,9 @@ set(${KIT}_TARGET_LIBRARIES vtkSlicer${MODULE_NAME}ModuleMRML vtkSlicerCamerasModuleLogic ${VTK_LIBRARIES} + qSlicerSubjectHierarchyModuleWidgets + qSlicerSegmentationsModuleWidgets + ) #----------------------------------------------------------------------------- diff --git a/VirtualReality/Widgets/Resources/StyleSheets/VrWidgetStyle.qss b/VirtualReality/Widgets/Resources/StyleSheets/VrWidgetStyle.qss new file mode 100644 index 0000000..ee037b4 --- /dev/null +++ b/VirtualReality/Widgets/Resources/StyleSheets/VrWidgetStyle.qss @@ -0,0 +1,50 @@ +QWidget { + font: 20px; +} + +QAbstractButton { + color: #3a3a3a; + border-color: #3a3a3a; + border-style: solid; + border-width: 1px; + border-radius: 10px; + padding: 4px; +} + +QAbstractButton:hover { + background-color:#ABABAB; +} + +QAbstractButton:pressed { + background-color: #525252; +} + +QSlider { + min-height: 68px; + max-height: 68px; + background: white; +} + +QSlider::groove:horizontal { + border: 1px solid #262626; + height: 5px; + background: #399aef; + margin: 0 12px; +} + +QSlider::handle:horizontal { + background: white; + border: 2px solid black; + border-radius: 10px; + width: 23px; + height: 100px; + margin: -24px -12px; +} + +QSlider::handle:horizontal:hover { + background: #ABABAB; +} + +QSlider::handle:horizontal:pressed { + background: #525252; +} diff --git a/VirtualReality/Widgets/Resources/UI/qMRMLVirtualRealityDataModuleWidget.ui b/VirtualReality/Widgets/Resources/UI/qMRMLVirtualRealityDataModuleWidget.ui new file mode 100644 index 0000000..1872872 --- /dev/null +++ b/VirtualReality/Widgets/Resources/UI/qMRMLVirtualRealityDataModuleWidget.ui @@ -0,0 +1,69 @@ + + + qMRMLVirtualRealityDataModuleWidget + + + + 0 + 0 + 413 + 256 + + + + VR Data + + + + 4 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + + + + qMRMLWidget + QWidget +
qMRMLWidget.h
+ 1 +
+ + qMRMLSubjectHierarchyTreeView + QTreeView +
qMRMLSubjectHierarchyTreeView.h
+
+
+ + + + qMRMLVirtualRealityDataModuleWidget + mrmlSceneChanged(vtkMRMLScene*) + SubjectHierarchyTreeView + setMRMLScene(vtkMRMLScene*) + + + 300 + 2 + + + 301 + 26 + + + + +
diff --git a/VirtualReality/Widgets/Resources/UI/qMRMLVirtualRealityHomeWidget.ui b/VirtualReality/Widgets/Resources/UI/qMRMLVirtualRealityHomeWidget.ui new file mode 100644 index 0000000..e846504 --- /dev/null +++ b/VirtualReality/Widgets/Resources/UI/qMRMLVirtualRealityHomeWidget.ui @@ -0,0 +1,208 @@ + + + qMRMLVirtualRealityHomeWidget + + + + 0 + 0 + 532 + 263 + + + + VR Home + + + + + + Back + + + + + + + QFrame::NoFrame + + + QFrame::Raised + + + + + + Sync view to reference view + + + + + + + Fly speed: + + + Qt::AlignBottom|Qt::AlignLeading|Qt::AlignLeft + + + + + + + 1 + + + 0.500000000000000 + + + 2.000000000000000 + + + 0.100000000000000 + + + 10.000000000000000 + + + 1.660000000000000 + + + m/s + + + + + + + Magnification: + + + Qt::AlignBottom|Qt::AlignLeading|Qt::AlignLeft + + + + + + + + + 10x + + + + + + + Lock magnification + + + + + + + 0.01x + + + + + + + 1x + + + + + + + 0.1x + + + + + + + 100x + + + + + + + + + + 0 + 13 + + + + Motion sensitivity: + + + Qt::AlignBottom|Qt::AlignLeading|Qt::AlignLeft + + + + + + + 0 + + + 100.000000000000000 + + + 50.000000000000000 + + + % + + + + + + + + + + Modules + + + + + + + + + + + + QFrame::NoFrame + + + QFrame::Raised + + + 0 + + + + + + + + qMRMLWidget + QWidget +
qMRMLWidget.h
+ 1 +
+ + ctkSliderWidget + QWidget +
ctkSliderWidget.h
+
+
+ + +
diff --git a/VirtualReality/Widgets/Resources/UI/qMRMLVirtualRealitySegmentEditorWidget.ui b/VirtualReality/Widgets/Resources/UI/qMRMLVirtualRealitySegmentEditorWidget.ui new file mode 100644 index 0000000..9cf629d --- /dev/null +++ b/VirtualReality/Widgets/Resources/UI/qMRMLVirtualRealitySegmentEditorWidget.ui @@ -0,0 +1,79 @@ + + + qMRMLVirtualRealitySegmentEditorWidget + + + + 0 + 0 + 589 + 590 + + + + + 100 + 0 + + + + qMRMLSegmentEditorWidget + + + + 4 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + + + + qMRMLWidget + QWidget +
qMRMLWidget.h
+ 1 +
+ + qMRMLSegmentEditorWidget + qMRMLWidget +
qMRMLSegmentEditorWidget.h
+ 1 +
+
+ + + + + + + qMRMLVirtualRealitySegmentEditorWidget + mrmlSceneChanged(vtkMRMLScene*) + SegmentEditorWidget + setMRMLScene(vtkMRMLScene*) + + + 189 + 581 + + + 189 + 533 + + + + +
diff --git a/VirtualReality/Widgets/Resources/qSlicerVirtualRealityModuleWidgets.qrc b/VirtualReality/Widgets/Resources/qSlicerVirtualRealityModuleWidgets.qrc new file mode 100644 index 0000000..9cb40ca --- /dev/null +++ b/VirtualReality/Widgets/Resources/qSlicerVirtualRealityModuleWidgets.qrc @@ -0,0 +1,5 @@ + + + StyleSheets/VrWidgetStyle.qss + + diff --git a/VirtualReality/Widgets/qMRMLVirtualRealityDataModuleWidget.cxx b/VirtualReality/Widgets/qMRMLVirtualRealityDataModuleWidget.cxx new file mode 100644 index 0000000..9a41f32 --- /dev/null +++ b/VirtualReality/Widgets/qMRMLVirtualRealityDataModuleWidget.cxx @@ -0,0 +1,90 @@ +/*============================================================================== + + Copyright (c) Laboratory for Percutaneous Surgery (PerkLab) + Queen's University, Kingston, ON, Canada. All Rights Reserved. + + See COPYRIGHT.txt + or http://www.slicer.org/copyright/copyright.txt for details. + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + This file was originally developed by Csaba Pinter, PerkLab, Queen's University + and was supported through the Applied Cancer Research Unit program of Cancer Care + Ontario with funds provided by the Ontario Ministry of Health and Long-Term Care + and CANARIE. + +==============================================================================*/ + +// VirtualReality Widgets includes +#include "qMRMLVirtualRealityDataModuleWidget.h" +#include "ui_qMRMLVirtualRealityDataModuleWidget.h" + +// VirtualReality MRML includes +#include "vtkMRMLVirtualRealityViewNode.h" + +// Qt includes +#include + +//----------------------------------------------------------------------------- +class qMRMLVirtualRealityDataModuleWidgetPrivate : public Ui_qMRMLVirtualRealityDataModuleWidget +{ + Q_DECLARE_PUBLIC(qMRMLVirtualRealityDataModuleWidget); + +protected: + qMRMLVirtualRealityDataModuleWidget* const q_ptr; + +public: + qMRMLVirtualRealityDataModuleWidgetPrivate(qMRMLVirtualRealityDataModuleWidget& object); + virtual ~qMRMLVirtualRealityDataModuleWidgetPrivate(); + + void init(); +}; + +//----------------------------------------------------------------------------- +qMRMLVirtualRealityDataModuleWidgetPrivate::qMRMLVirtualRealityDataModuleWidgetPrivate(qMRMLVirtualRealityDataModuleWidget& object) + : q_ptr(&object) +{ +} + +//----------------------------------------------------------------------------- +qMRMLVirtualRealityDataModuleWidgetPrivate::~qMRMLVirtualRealityDataModuleWidgetPrivate() +{ +} + +//----------------------------------------------------------------------------- +void qMRMLVirtualRealityDataModuleWidgetPrivate::init() +{ + Q_Q(qMRMLVirtualRealityDataModuleWidget); + this->setupUi(q); + + // Customize widget contents + this->SubjectHierarchyTreeView->setColumnHidden(this->SubjectHierarchyTreeView->model()->idColumn(), true); +} + +//----------------------------------------------------------------------------- +// qMRMLVirtualRealityDataModuleWidget methods + +//----------------------------------------------------------------------------- +qMRMLVirtualRealityDataModuleWidget::qMRMLVirtualRealityDataModuleWidget(QWidget* _parent) + : qMRMLWidget(_parent) + , d_ptr(new qMRMLVirtualRealityDataModuleWidgetPrivate(*this)) +{ + Q_D(qMRMLVirtualRealityDataModuleWidget); + d->init(); + + this->updateWidgetFromMRML(); +} + +//----------------------------------------------------------------------------- +qMRMLVirtualRealityDataModuleWidget::~qMRMLVirtualRealityDataModuleWidget() += default; + +//----------------------------------------------------------------------------- +void qMRMLVirtualRealityDataModuleWidget::updateWidgetFromMRML() +{ + //Q_D(qMRMLVirtualRealityDataModuleWidget); +} diff --git a/VirtualReality/Widgets/qMRMLVirtualRealityDataModuleWidget.h b/VirtualReality/Widgets/qMRMLVirtualRealityDataModuleWidget.h new file mode 100644 index 0000000..16173b1 --- /dev/null +++ b/VirtualReality/Widgets/qMRMLVirtualRealityDataModuleWidget.h @@ -0,0 +1,68 @@ +/*============================================================================== + + Copyright (c) Laboratory for Percutaneous Surgery (PerkLab) + Queen's University, Kingston, ON, Canada. All Rights Reserved. + + See COPYRIGHT.txt + or http://www.slicer.org/copyright/copyright.txt for details. + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + This file was originally developed by Csaba Pinter, PerkLab, Queen's University + and was supported through the Applied Cancer Research Unit program of Cancer Care + Ontario with funds provided by the Ontario Ministry of Health and Long-Term Care + and CANARIE. + +==============================================================================*/ + +#ifndef __qMRMLVirtualRealityDataModuleWidget_h +#define __qMRMLVirtualRealityDataModuleWidget_h + +// VirtualReality Widgets includes +#include "qSlicerVirtualRealityModuleWidgetsExport.h" + +// MRMLWidgets includes +#include "qMRMLWidget.h" + +// CTK includes +#include +#include + +// MRML includes +#include "qMRMLSubjectHierarchyTreeView.h" +#include "qMRMLSubjectHierarchyModel.h" + +class qMRMLVirtualRealityDataModuleWidgetPrivate; + +/// \ingroup SlicerVirtualReality_Widgets +class Q_SLICER_QTMODULES_VIRTUALREALITY_WIDGETS_EXPORT qMRMLVirtualRealityDataModuleWidget : public qMRMLWidget +{ + Q_OBJECT + QVTK_OBJECT + +public: + typedef qMRMLWidget Superclass; + /// Constructor + explicit qMRMLVirtualRealityDataModuleWidget(QWidget* parent = nullptr); + /// Destructor + ~qMRMLVirtualRealityDataModuleWidget() override; + +public slots: + +protected slots: + /// Update widgets from the MRML node + void updateWidgetFromMRML(); + +protected: + QScopedPointer d_ptr; + +private: + Q_DECLARE_PRIVATE(qMRMLVirtualRealityDataModuleWidget); + Q_DISABLE_COPY(qMRMLVirtualRealityDataModuleWidget); +}; + +#endif diff --git a/VirtualReality/Widgets/qMRMLVirtualRealityHomeWidget.cxx b/VirtualReality/Widgets/qMRMLVirtualRealityHomeWidget.cxx new file mode 100644 index 0000000..a3704ce --- /dev/null +++ b/VirtualReality/Widgets/qMRMLVirtualRealityHomeWidget.cxx @@ -0,0 +1,423 @@ +/*============================================================================== + + Copyright (c) Laboratory for Percutaneous Surgery (PerkLab) + Queen's University, Kingston, ON, Canada. All Rights Reserved. + + See COPYRIGHT.txt + or http://www.slicer.org/copyright/copyright.txt for details. + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + This file was originally developed by Csaba Pinter, PerkLab, Queen's University + and was supported through the Applied Cancer Research Unit program of Cancer Care + Ontario with funds provided by the Ontario Ministry of Health and Long-Term Care + and CANARIE. + +==============================================================================*/ + +// VirtualReality Widgets includes +#include "qMRMLVirtualRealityHomeWidget.h" +#include "ui_qMRMLVirtualRealityHomeWidget.h" +#include "qMRMLVirtualRealityDataModuleWidget.h" +#include "qMRMLVirtualRealitySegmentEditorWidget.h" + +// VirtualReality MRML includes +#include "vtkMRMLVirtualRealityViewNode.h" + +// Segmentations includes +#include "vtkMRMLSegmentEditorNode.h" + +// VTK includes +#include + +// Qt includes +#include +#include + +//----------------------------------------------------------------------------- +class qMRMLVirtualRealityHomeWidgetPrivate : public Ui_qMRMLVirtualRealityHomeWidget +{ + Q_DECLARE_PUBLIC(qMRMLVirtualRealityHomeWidget); + +protected: + qMRMLVirtualRealityHomeWidget* const q_ptr; + +public: + qMRMLVirtualRealityHomeWidgetPrivate(qMRMLVirtualRealityHomeWidget& object); + virtual ~qMRMLVirtualRealityHomeWidgetPrivate(); + + void init(); + + /// Association between VR module buttons and the corresponding widgets + QMap ModuleWidgetsMap; + +public: + /// Virtual reality view MRML node + vtkWeakPointer VirtualRealityViewNode; + qMRMLVirtualRealityDataModuleWidget* DataModuleWidget; + qMRMLVirtualRealitySegmentEditorWidget* SegmentEditorWidget; +}; + +//----------------------------------------------------------------------------- +qMRMLVirtualRealityHomeWidgetPrivate::qMRMLVirtualRealityHomeWidgetPrivate(qMRMLVirtualRealityHomeWidget& object) + : q_ptr(&object) +{ + this->VirtualRealityViewNode = nullptr; + this->DataModuleWidget = nullptr; + this->SegmentEditorWidget = nullptr; +} + +//----------------------------------------------------------------------------- +qMRMLVirtualRealityHomeWidgetPrivate::~qMRMLVirtualRealityHomeWidgetPrivate() +{ + this->VirtualRealityViewNode = nullptr; +} + +//----------------------------------------------------------------------------- +void qMRMLVirtualRealityHomeWidgetPrivate::init() +{ + Q_Q(qMRMLVirtualRealityHomeWidget); + this->setupUi(q); + + this->backButton->setVisible(false); + + QObject::connect(this->MotionSensitivitySliderWidget, SIGNAL(valueChanged(double)), q, SLOT(onMotionSensitivityChanged(double))); + QObject::connect(this->FlySpeedSliderWidget, SIGNAL(valueChanged(double)), q, SLOT(onFlySpeedChanged(double))); + QObject::connect(this->Magnification001xButton, SIGNAL(clicked()), q, SLOT(onMagnification001xPressed())); + QObject::connect(this->Magnification01xButton, SIGNAL(clicked()), q, SLOT(onMagnification01xPressed())); + QObject::connect(this->Magnification1xButton, SIGNAL(clicked()), q, SLOT(onMagnification1xPressed())); + QObject::connect(this->Magnification10xButton, SIGNAL(clicked()), q, SLOT(onMagnification10xPressed())); + QObject::connect(this->Magnification100xButton, SIGNAL(clicked()), q, SLOT(onMagnification100xPressed())); + QObject::connect(this->SyncViewToReferenceViewButton, SIGNAL(clicked()), q, SLOT(updateViewFromReferenceViewCamera())); + + //QObject::connect(this->LockMagnificationCheckBox, SIGNAL(toggled(bool)), q, SLOT(setMagnificationLock(bool))); + //TODO: Magnification lock of view node not implemented yet + + // Hide module widget frame. It appears with the module when a module button is clicked + this->ModuleWidgetFrame->setVisible(false); + // Setup module widget frame layout + QVBoxLayout* moduleWidgetFrameLayout = new QVBoxLayout(this->ModuleWidgetFrame); + + // Register default VR modules + q->registerDefaultModules(); +} + +//----------------------------------------------------------------------------- +// qMRMLVirtualRealityHomeWidget methods + +//----------------------------------------------------------------------------- + +qMRMLVirtualRealityHomeWidget::qMRMLVirtualRealityHomeWidget(QWidget* _parent) + : qMRMLWidget(_parent) + , d_ptr(new qMRMLVirtualRealityHomeWidgetPrivate(*this)) +{ + Q_D(qMRMLVirtualRealityHomeWidget); + d->init(); + this->updateWidgetFromMRML(); +} + +//----------------------------------------------------------------------------- +qMRMLVirtualRealityHomeWidget::~qMRMLVirtualRealityHomeWidget() += default; + +//----------------------------------------------------------------------------- +vtkMRMLVirtualRealityViewNode* qMRMLVirtualRealityHomeWidget::virtualRealityViewNode() const +{ + Q_D(const qMRMLVirtualRealityHomeWidget); + return d->VirtualRealityViewNode; +} + +//----------------------------------------------------------------------------- +QString qMRMLVirtualRealityHomeWidget::virtualRealityViewNodeID()const +{ + Q_D(const qMRMLVirtualRealityHomeWidget); + return (d->VirtualRealityViewNode.GetPointer() ? d->VirtualRealityViewNode->GetID() : QString()); +} + +//----------------------------------------------------------------------------- +void qMRMLVirtualRealityHomeWidget::setVirtualRealityViewNode(vtkMRMLVirtualRealityViewNode* node) +{ + Q_D(qMRMLVirtualRealityHomeWidget); + + if (d->VirtualRealityViewNode == node) + { + return; + } + + qvtkReconnect(d->VirtualRealityViewNode, node, vtkCommand::ModifiedEvent, this, SLOT(updateWidgetFromMRML())); + + vtkMRMLVirtualRealityViewNode* vrViewNode = vtkMRMLVirtualRealityViewNode::SafeDownCast(node); + d->VirtualRealityViewNode = vrViewNode; + + this->updateWidgetFromMRML(); +} + +//----------------------------------------------------------------------------- +void qMRMLVirtualRealityHomeWidget::updateWidgetFromMRML() +{ + Q_D(qMRMLVirtualRealityHomeWidget); + vtkMRMLVirtualRealityViewNode* vrViewNode = d->VirtualRealityViewNode; + + bool wasBlocked = d->MotionSensitivitySliderWidget->blockSignals(true); + d->MotionSensitivitySliderWidget->setValue(vrViewNode != nullptr ? vrViewNode->GetMotionSensitivity() * 100.0 : 0); + d->MotionSensitivitySliderWidget->setEnabled(vrViewNode != nullptr); + d->MotionSensitivitySliderWidget->blockSignals(wasBlocked); + + wasBlocked = d->FlySpeedSliderWidget->blockSignals(true); + d->FlySpeedSliderWidget->setValue(vrViewNode != nullptr ? vrViewNode->GetMotionSpeed() : 1.6666); + d->FlySpeedSliderWidget->setEnabled(vrViewNode != nullptr); + d->FlySpeedSliderWidget->blockSignals(wasBlocked); + + /* + bool wasBlocked = d->LockMagnificationCheckBox->blockSignals(true); + d->LockMagnificationCheckBox->setChecked(vrViewNode != nullptr && vrViewNode->); + d->LockMagnificationCheckBox->setEnabled(vrViewNode != nullptr); + d->LockMagnificationCheckBox->blockSignals(wasBlocked); + */ + //TODO: Magnification lock of view node not implemented yet + + d->Magnification001xButton->setEnabled(vrViewNode != nullptr && vrViewNode->GetMagnification() != NULL); + d->Magnification01xButton->setEnabled(vrViewNode != nullptr && vrViewNode->GetMagnification() != NULL); + d->Magnification1xButton->setEnabled(vrViewNode != nullptr && vrViewNode->GetMagnification() != NULL); + d->Magnification10xButton->setEnabled(vrViewNode != nullptr && vrViewNode->GetMagnification() != NULL); + d->Magnification100xButton->setEnabled(vrViewNode != nullptr && vrViewNode->GetMagnification() != NULL); +} + +//----------------------------------------------------------------------------- +void qMRMLVirtualRealityHomeWidget::onMotionSensitivityChanged(double percent) +{ + Q_D(qMRMLVirtualRealityHomeWidget); + vtkMRMLVirtualRealityViewNode* vrViewNode = d->VirtualRealityViewNode; + + if (!vrViewNode) + { + qCritical() << Q_FUNC_INFO << " Failed: view node is null"; + return; + } + vrViewNode->SetMotionSensitivity(percent * 0.01); +} + +//----------------------------------------------------------------------------- +void qMRMLVirtualRealityHomeWidget::onFlySpeedChanged(double speedMps) +{ + Q_D(qMRMLVirtualRealityHomeWidget); + vtkMRMLVirtualRealityViewNode* vrViewNode = d->VirtualRealityViewNode; + + if (!vrViewNode) + { + qCritical() << Q_FUNC_INFO << " Failed: view node is null"; + return; + } + vrViewNode->SetMotionSpeed(speedMps); +} + +//----------------------------------------------------------------------------- +void qMRMLVirtualRealityHomeWidget::onMagnification001xPressed() +{ + Q_D(qMRMLVirtualRealityHomeWidget); + vtkMRMLVirtualRealityViewNode* vrViewNode = d->VirtualRealityViewNode; + + if (!vrViewNode) + { + qCritical() << Q_FUNC_INFO << " Failed: view node is null"; + return; + } + vrViewNode->SetMagnification(0.01); +} + +//----------------------------------------------------------------------------- +void qMRMLVirtualRealityHomeWidget::onMagnification01xPressed() +{ + Q_D(qMRMLVirtualRealityHomeWidget); + vtkMRMLVirtualRealityViewNode* vrViewNode = d->VirtualRealityViewNode; + + if (!vrViewNode) + { + qCritical() << Q_FUNC_INFO << " Failed: view node is null"; + return; + } + vrViewNode->SetMagnification(0.1); +} + +//----------------------------------------------------------------------------- +void qMRMLVirtualRealityHomeWidget::onMagnification1xPressed() +{ + Q_D(qMRMLVirtualRealityHomeWidget); + vtkMRMLVirtualRealityViewNode* vrViewNode = d->VirtualRealityViewNode; + + if (!vrViewNode) + { + qCritical() << Q_FUNC_INFO << " Failed: view node is null"; + return; + } + vrViewNode->SetMagnification(1.0); +} + +//----------------------------------------------------------------------------- +void qMRMLVirtualRealityHomeWidget::onMagnification10xPressed() +{ + Q_D(qMRMLVirtualRealityHomeWidget); + vtkMRMLVirtualRealityViewNode* vrViewNode = d->VirtualRealityViewNode; + + if (!vrViewNode) + { + qCritical() << Q_FUNC_INFO << " Failed: view node is null"; + return; + } + vrViewNode->SetMagnification(10.0); +} + +//----------------------------------------------------------------------------- +void qMRMLVirtualRealityHomeWidget::onMagnification100xPressed() +{ + Q_D(qMRMLVirtualRealityHomeWidget); + vtkMRMLVirtualRealityViewNode* vrViewNode = d->VirtualRealityViewNode; + + if (!vrViewNode) + { + qCritical() << Q_FUNC_INFO << " Failed: view node is null"; + return; + } + vrViewNode->SetMagnification(100.0); +} + +//----------------------------------------------------------------------------- +/* +void qMRMLVirtualRealityHomeWidget::updateViewFromReferenceViewCamera() +{ + Q_D(qMRMLVirtualRealityHomeWidget); + qSlicerVirtualRealityModule* vrModule = dynamic_cast(this->module()); + if (!vrModule) + { + qCritical() << Q_FUNC_INFO << " Failed: vrModule is null"; + return; + } + qMRMLVirtualRealityView* vrView = vrModule->viewWidget(); + if (!vrView) + { + qCritical() << Q_FUNC_INFO << " Failed: view node is null"; + return; + } + vrView->updateViewFromReferenceViewCamera(); +} +*/ +//TODO: This member function won't work unless qSlicerVirtualRealityModule and qMRMLVirtualRealityView are included + +//----------------------------------------------------------------------------- +/* +void qMRMLVirtualRealityHomeWidget::setMagnificationLock(bool active) +{ + Q_D(qMRMLVirtualRealityHomeWidget); + vtkMRMLVirtualRealityViewNode* vrViewNode = d->VirtualRealityViewNode; + + if (!vrViewNode) + { + qCritical() << Q_FUNC_INFO << " Failed: view node is null"; + return; + } + + vrViewNode->????(active); //TODO: Implement magnification lock for view node +} +*/ + +//----------------------------------------------------------------------------- +void qMRMLVirtualRealityHomeWidget::addModuleButton(QWidget* moduleWidget, QIcon& icon) +{ + Q_D(qMRMLVirtualRealityHomeWidget); + + if (!moduleWidget) + { + qCritical() << Q_FUNC_INFO << ": Invalid module widget given"; + return; + } + + // Create button for the new module + QPushButton* moduleButton = new QPushButton(d->ModulesGroupBox); + d->ModulesGroupBoxLayout->addWidget(moduleButton); + moduleButton->setIcon(icon); + + // Setup module widget within home widget + moduleWidget->setParent(d->ModuleWidgetFrame); + moduleWidget->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Expanding); + moduleWidget->setVisible(false); + d->ModuleWidgetFrame->layout()->addWidget(moduleWidget); + d->ModuleWidgetsMap[moduleButton] = moduleWidget; + + // Handle scene if a MRML widget is given + qMRMLWidget* moduleMrmlWidget = qobject_cast(moduleWidget); + if (moduleMrmlWidget) + { + moduleMrmlWidget->setMRMLScene(this->mrmlScene()); + QObject::connect(this, SIGNAL(mrmlSceneChanged(vtkMRMLScene*)), moduleWidget, SLOT(setMRMLScene(vtkMRMLScene*))); + } + + QObject::connect(moduleButton, SIGNAL(clicked()), this, SLOT(onModuleButtonClicked())); + QObject::connect(d->backButton, SIGNAL(clicked()), this, SLOT(onBackButtonClicked())); + + moduleButton->setVisible(true); + d->backButton->setVisible(false); +} + +//----------------------------------------------------------------------------- +void qMRMLVirtualRealityHomeWidget::onModuleButtonClicked() +{ + Q_D(qMRMLVirtualRealityHomeWidget); + + QPushButton* moduleButton = qobject_cast(sender()); + if (!moduleButton) + { + qCritical() << Q_FUNC_INFO << ": Failed to access clicked button"; + return; + } + + d->HomeWidgetFrame->setVisible(false); + d->ModulesGroupBox->setVisible(false); + + d->ModuleWidgetFrame->setVisible(true); + d->ModuleWidgetsMap[moduleButton]->setVisible(true); + d->backButton->setVisible(true); +} + +//----------------------------------------------------------------------------- +void qMRMLVirtualRealityHomeWidget::onBackButtonClicked() +{ + Q_D(qMRMLVirtualRealityHomeWidget); + + QMap::const_iterator buttonIt; + for (buttonIt = d->ModuleWidgetsMap.constBegin(); buttonIt != d->ModuleWidgetsMap.constEnd(); ++buttonIt) + { + buttonIt.value()->setVisible(false); + } + d->ModuleWidgetFrame->setVisible(false); + d->backButton->setVisible(false); + + d->HomeWidgetFrame->setVisible(true); + d->ModulesGroupBox->setVisible(true); +} + +//----------------------------------------------------------------------------- +void qMRMLVirtualRealityHomeWidget::registerDefaultModules() +{ + Q_D(qMRMLVirtualRealityHomeWidget); + + d->DataModuleWidget = new qMRMLVirtualRealityDataModuleWidget(this); + d->DataModuleWidget->setMRMLScene(this->mrmlScene()); + d->SegmentEditorWidget = new qMRMLVirtualRealitySegmentEditorWidget(this); + d->SegmentEditorWidget->setMRMLScene(this->mrmlScene()); + + QIcon dataIcon(QPixmap(":/Icons/SubjectHierarchy.png")); + QIcon segmentEditorIcon(QPixmap(":/Icons/SegmentEditor.png")); + + this->addModuleButton(d->DataModuleWidget, dataIcon); + this->addModuleButton(d->SegmentEditorWidget, segmentEditorIcon); +} + +void qMRMLVirtualRealityHomeWidget::setMRMLSegmentEditorNode(vtkMRMLSegmentEditorNode* newSegmentEditorNode) +{ + Q_D(qMRMLVirtualRealityHomeWidget); + d->SegmentEditorWidget->setMRMLSegmentEditorNode(newSegmentEditorNode); +} diff --git a/VirtualReality/Widgets/qMRMLVirtualRealityHomeWidget.h b/VirtualReality/Widgets/qMRMLVirtualRealityHomeWidget.h new file mode 100644 index 0000000..31e4559 --- /dev/null +++ b/VirtualReality/Widgets/qMRMLVirtualRealityHomeWidget.h @@ -0,0 +1,101 @@ +/*============================================================================== + + Copyright (c) Laboratory for Percutaneous Surgery (PerkLab) + Queen's University, Kingston, ON, Canada. All Rights Reserved. + + See COPYRIGHT.txt + or http://www.slicer.org/copyright/copyright.txt for details. + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + This file was originally developed by Csaba Pinter, PerkLab, Queen's University + and was supported through the Applied Cancer Research Unit program of Cancer Care + Ontario with funds provided by the Ontario Ministry of Health and Long-Term Care + and CANARIE. + +==============================================================================*/ + +#ifndef __qMRMLVirtualRealityHomeWidget_h +#define __qMRMLVirtualRealityHomeWidget_h + +// VirtualReality Widgets includes +#include "qSlicerVirtualRealityModuleWidgetsExport.h" + +// MRMLWidgets includes +#include "qMRMLWidget.h" + +// CTK includes +#include +#include + +// Qt includes +#include +#include + +class vtkMRMLVirtualRealityViewNode; +class qMRMLVirtualRealityHomeWidgetPrivate; +class vtkMRMLSegmentEditorNode; + +/// \ingroup SlicerVirtualReality_Widgets +class Q_SLICER_QTMODULES_VIRTUALREALITY_WIDGETS_EXPORT qMRMLVirtualRealityHomeWidget : public qMRMLWidget +{ + Q_OBJECT + QVTK_OBJECT + +public: + typedef qMRMLWidget Superclass; + /// Constructor + explicit qMRMLVirtualRealityHomeWidget(QWidget* parent = nullptr); + /// Destructor + ~qMRMLVirtualRealityHomeWidget() override; + + /// Get virtual reality view MRML node + Q_INVOKABLE vtkMRMLVirtualRealityViewNode* virtualRealityViewNode()const; + /// Get virtual reality view MRML node ID + Q_INVOKABLE QString virtualRealityViewNodeID()const; + + /// Add new button for a given VR module widget + /// \param moduleWidget The widget that appears in place of the home widget when its button is pressed + /// \param icon The icon that appears on the button in the home widget + Q_INVOKABLE void addModuleButton(QWidget* moduleWidget, QIcon& icon); + + /// Register default modules: Data, Segment Editor + Q_INVOKABLE void registerDefaultModules(); + + /// Set the segment editor node for the segment editor widget + Q_INVOKABLE void setMRMLSegmentEditorNode(vtkMRMLSegmentEditorNode* newSegmentEditorNode); + +public slots: + /// Set virtual reality view MRML node + void setVirtualRealityViewNode(vtkMRMLVirtualRealityViewNode* node); + + void onMotionSensitivityChanged(double); + void onFlySpeedChanged(double); + void onMagnification001xPressed(); + void onMagnification01xPressed(); + void onMagnification1xPressed(); + void onMagnification10xPressed(); + void onMagnification100xPressed(); + //void updateViewFromReferenceViewCamera(); + //void setMagnificationLock(bool); + + void onModuleButtonClicked(); + void onBackButtonClicked(); + +protected slots: + /// Update widgets from the MRML node + void updateWidgetFromMRML(); + +protected: + QScopedPointer d_ptr; + +private: + Q_DECLARE_PRIVATE(qMRMLVirtualRealityHomeWidget); + Q_DISABLE_COPY(qMRMLVirtualRealityHomeWidget); +}; + +#endif diff --git a/VirtualReality/Widgets/qMRMLVirtualRealitySegmentEditorWidget.cxx b/VirtualReality/Widgets/qMRMLVirtualRealitySegmentEditorWidget.cxx new file mode 100644 index 0000000..5640241 --- /dev/null +++ b/VirtualReality/Widgets/qMRMLVirtualRealitySegmentEditorWidget.cxx @@ -0,0 +1,118 @@ +/*============================================================================== + + Copyright (c) Laboratory for Percutaneous Surgery (PerkLab) + Queen's University, Kingston, ON, Canada. All Rights Reserved. + + See COPYRIGHT.txt + or http://www.slicer.org/copyright/copyright.txt for details. + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + This file was originally developed by Csaba Pinter, PerkLab, Queen's University + and was supported through the Applied Cancer Research Unit program of Cancer Care + Ontario with funds provided by the Ontario Ministry of Health and Long-Term Care + and CANARIE. + +==============================================================================*/ + +// VirtualReality Widgets includes +#include "qMRMLVirtualRealitySegmentEditorWidget.h" +#include "ui_qMRMLVirtualRealitySegmentEditorWidget.h" + +// VirtualReality MRML includes +#include "vtkMRMLVirtualRealityViewNode.h" + +// Segmentations includes +#include "vtkMRMLSegmentEditorNode.h" + +// Qt includes +#include +#include "qpushbutton.h" +#include "ctkMenuButton.h" +#include "qtoolbutton.h" + +//----------------------------------------------------------------------------- +class qMRMLVirtualRealitySegmentEditorWidgetPrivate : public Ui_qMRMLVirtualRealitySegmentEditorWidget +{ + Q_DECLARE_PUBLIC(qMRMLVirtualRealitySegmentEditorWidget); + +protected: + qMRMLVirtualRealitySegmentEditorWidget* const q_ptr; + +public: + qMRMLVirtualRealitySegmentEditorWidgetPrivate(qMRMLVirtualRealitySegmentEditorWidget& object); + virtual ~qMRMLVirtualRealitySegmentEditorWidgetPrivate(); + + void init(); +}; + +//----------------------------------------------------------------------------- +qMRMLVirtualRealitySegmentEditorWidgetPrivate::qMRMLVirtualRealitySegmentEditorWidgetPrivate(qMRMLVirtualRealitySegmentEditorWidget& object) + : q_ptr(&object) +{ +} + +//----------------------------------------------------------------------------- +qMRMLVirtualRealitySegmentEditorWidgetPrivate::~qMRMLVirtualRealitySegmentEditorWidgetPrivate() +{ +} + +//----------------------------------------------------------------------------- +void qMRMLVirtualRealitySegmentEditorWidgetPrivate::init() +{ + Q_Q(qMRMLVirtualRealitySegmentEditorWidget); + this->setupUi(q); + + //List of effects to be used in VR; effects not listed here are not shown in the widget + QStringList effectNameOrder; + effectNameOrder.append("None"); + effectNameOrder.append("Paint"); + effectNameOrder.append("Erase"); + effectNameOrder.append("Level tracing"); + effectNameOrder.append("Grow from seeds"); + effectNameOrder.append("Smoothing"); + effectNameOrder.append("Scissors"); + effectNameOrder.append("Logical Operators"); + this->SegmentEditorWidget->setEffectNameOrder(effectNameOrder); + + //Hide unneeded effects and buttons + this->SegmentEditorWidget->setUnorderedEffectsVisible(false); + this->SegmentEditorWidget->setSwitchToSegmentationsButtonVisible(false); + this->SegmentEditorWidget->findChild("SpecifyGeometryButton")->setVisible(false); + this->SegmentEditorWidget->findChild("SliceRotateWarningButton")->setVisible(false); + this->SegmentEditorWidget->findChild("Show3DButton")->setVisible(false); +} + +//----------------------------------------------------------------------------- +// qMRMLVirtualRealitySegmentEditorWidget methods + +//----------------------------------------------------------------------------- +qMRMLVirtualRealitySegmentEditorWidget::qMRMLVirtualRealitySegmentEditorWidget(QWidget* _parent) + : qMRMLWidget(_parent) + , d_ptr(new qMRMLVirtualRealitySegmentEditorWidgetPrivate(*this)) +{ + Q_D(qMRMLVirtualRealitySegmentEditorWidget); + d->init(); + + this->updateWidgetFromMRML(); +} + +//----------------------------------------------------------------------------- +qMRMLVirtualRealitySegmentEditorWidget::~qMRMLVirtualRealitySegmentEditorWidget() += default; + +//----------------------------------------------------------------------------- +void qMRMLVirtualRealitySegmentEditorWidget::updateWidgetFromMRML() +{ + //Q_D(qMRMLVirtualRealitySegmentEditorWidget); +} + +void qMRMLVirtualRealitySegmentEditorWidget::setMRMLSegmentEditorNode(vtkMRMLSegmentEditorNode* newSegmentEditorNode) +{ + Q_D(qMRMLVirtualRealitySegmentEditorWidget); + d->SegmentEditorWidget->setMRMLSegmentEditorNode(newSegmentEditorNode); +} diff --git a/VirtualReality/Widgets/qMRMLVirtualRealitySegmentEditorWidget.h b/VirtualReality/Widgets/qMRMLVirtualRealitySegmentEditorWidget.h new file mode 100644 index 0000000..93c10f9 --- /dev/null +++ b/VirtualReality/Widgets/qMRMLVirtualRealitySegmentEditorWidget.h @@ -0,0 +1,67 @@ +/*============================================================================== + + Copyright (c) Laboratory for Percutaneous Surgery (PerkLab) + Queen's University, Kingston, ON, Canada. All Rights Reserved. + + See COPYRIGHT.txt + or http://www.slicer.org/copyright/copyright.txt for details. + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + This file was originally developed by Csaba Pinter, PerkLab, Queen's University + and was supported through the Applied Cancer Research Unit program of Cancer Care + Ontario with funds provided by the Ontario Ministry of Health and Long-Term Care + and CANARIE. + +==============================================================================*/ + +#ifndef __qMRMLVirtualRealitySegmentEditorWidget_h +#define __qMRMLVirtualRealitySegmentEditorWidget_h + +// VirtualReality Widgets includes +#include "qSlicerVirtualRealityModuleWidgetsExport.h" + +// MRMLWidgets includes +#include "qMRMLWidget.h" + +// CTK includes +#include +#include + +class qMRMLVirtualRealitySegmentEditorWidgetPrivate; +class vtkMRMLSegmentEditorNode; + +/// \ingroup SlicerVirtualReality_Widgets +class Q_SLICER_QTMODULES_VIRTUALREALITY_WIDGETS_EXPORT qMRMLVirtualRealitySegmentEditorWidget : public qMRMLWidget +{ + Q_OBJECT + QVTK_OBJECT + +public: + typedef qMRMLWidget Superclass; + /// Constructor + explicit qMRMLVirtualRealitySegmentEditorWidget(QWidget* parent = nullptr); + /// Destructor + ~qMRMLVirtualRealitySegmentEditorWidget() override; + + void setMRMLSegmentEditorNode(vtkMRMLSegmentEditorNode* newSegmentEditorNode); + +public slots: + +protected slots: + /// Update widgets from the MRML node + void updateWidgetFromMRML(); + +protected: + QScopedPointer d_ptr; + +private: + Q_DECLARE_PRIVATE(qMRMLVirtualRealitySegmentEditorWidget); + Q_DISABLE_COPY(qMRMLVirtualRealitySegmentEditorWidget); +}; + +#endif diff --git a/VirtualReality/Widgets/qMRMLVirtualRealityView.cxx b/VirtualReality/Widgets/qMRMLVirtualRealityView.cxx index ef2fbae..556caf5 100644 --- a/VirtualReality/Widgets/qMRMLVirtualRealityView.cxx +++ b/VirtualReality/Widgets/qMRMLVirtualRealityView.cxx @@ -56,8 +56,9 @@ #include "vtkSlicerConfigure.h" // For Slicer_USE_OpenVR #include "vtkSlicerCamerasModuleLogic.h" -// VirtualReality includes +// VirtualReality MRML includes #include "vtkMRMLVirtualRealityViewNode.h" +#include "vtkMRMLThreeDViewInteractorStyle.h" // MRMLDisplayableManager includes #include @@ -114,8 +115,9 @@ namespace qMRMLVirtualRealityViewPrivate::qMRMLVirtualRealityViewPrivate(qMRMLVirtualRealityView& object) : q_ptr(&object) , CamerasLogic(nullptr) + , MRMLVirtualRealityViewNode(nullptr) + , HomeWidget(nullptr) { - this->MRMLVirtualRealityViewNode = nullptr; } //--------------------------------------------------------------------------- @@ -127,6 +129,10 @@ qMRMLVirtualRealityViewPrivate::~qMRMLVirtualRealityViewPrivate() void qMRMLVirtualRealityViewPrivate::init() { QObject::connect(&this->VirtualRealityLoopTimer, SIGNAL(timeout()), this, SLOT(doOpenVirtualReality())); + + // Setup VR home widget + this->HomeWidget = new qMRMLVirtualRealityHomeWidget(q_ptr); + QObject::connect(this, SIGNAL(mrmlSceneChanged(vtkMRMLScene*)), this->HomeWidget, SLOT(setMRMLScene(vtkMRMLScene*))); } //---------------------------------------------------------------------------- @@ -691,6 +697,14 @@ qMRMLVirtualRealityView::~qMRMLVirtualRealityView() { } +//------------------------------------------------------------------------------ +void qMRMLVirtualRealityView::registerModule(QWidget* widget, QIcon& icon) +{ + Q_D(qMRMLVirtualRealityView); + + d->HomeWidget->addModuleButton(widget, icon); +} + //------------------------------------------------------------------------------ void qMRMLVirtualRealityView::addDisplayableManager(const QString& displayableManagerName) { @@ -730,6 +744,13 @@ vtkMRMLVirtualRealityViewNode* qMRMLVirtualRealityView::mrmlVirtualRealityViewNo return d->MRMLVirtualRealityViewNode; } +//---------------------------------------------------------------------------- +qMRMLVirtualRealityHomeWidget* qMRMLVirtualRealityView::vrHomeWidget()const +{ + Q_D(const qMRMLVirtualRealityView); + return d->HomeWidget; +} + //------------------------------------------------------------------------------ void qMRMLVirtualRealityView::getDisplayableManagers(vtkCollection* displayableManagers) { @@ -1006,3 +1027,17 @@ void qMRMLVirtualRealityView::updateViewFromReferenceViewCamera() ren->ResetCameraClippingRange(); } + +//------------------------------------------------------------------------------ +void qMRMLVirtualRealityView::setVirtualWidget(QWidget* menuWidget) +{ + QPixmap menuTexture(menuWidget->size()); + //TODO: Set VR style sheet on widget (large text etc) + menuWidget->render(&menuTexture); + + bool errorCheck = menuTexture.save("menuTextureImage.png", "PNG", 100); + if (!errorCheck) + { + qCritical() << Q_FUNC_INFO << ": Error while saving menu texture"; + } +} diff --git a/VirtualReality/Widgets/qMRMLVirtualRealityView.h b/VirtualReality/Widgets/qMRMLVirtualRealityView.h index d7fc2d7..dc78e23 100644 --- a/VirtualReality/Widgets/qMRMLVirtualRealityView.h +++ b/VirtualReality/Widgets/qMRMLVirtualRealityView.h @@ -31,8 +31,9 @@ #include "qSlicerVirtualRealityModuleWidgetsExport.h" // CTK includes -#include +#include +class qMRMLVirtualRealityHomeWidget; class qMRMLVirtualRealityViewPrivate; class vtkMRMLVirtualRealityViewNode; class vtkCollection; @@ -84,6 +85,9 @@ class Q_SLICER_QTMODULES_VIRTUALREALITY_WIDGETS_EXPORT qMRMLVirtualRealityView : /// Get the 3D View node observed by view. Q_INVOKABLE vtkMRMLVirtualRealityViewNode* mrmlVirtualRealityViewNode()const; + /// Get VR home widget + Q_INVOKABLE qMRMLVirtualRealityHomeWidget* vrHomeWidget()const; + /// Get a reference to the associated vtkRenderer vtkOpenVRRenderer* renderer()const; @@ -121,6 +125,10 @@ class Q_SLICER_QTMODULES_VIRTUALREALITY_WIDGETS_EXPORT qMRMLVirtualRealityView : Q_INVOKABLE void setGestureButtonToNone(); ///@} + /// Register VR module. A button will be added in the modules section of the VR home + /// widget with the provided icon. When clicked, the given widget will be shown + void registerModule(QWidget* widget, QIcon& icon); + signals: void physicalToWorldMatrixModified(); @@ -150,8 +158,10 @@ public slots: void onPhysicalToWorldMatrixModified(); void onButton3DEvent(vtkObject* caller, void* call_data, unsigned long vtk_event, void* client_data); -protected: + /// Set widget that is being shown on the "tablet panel" in virtual reality + void setVirtualWidget(QWidget*); +protected: QScopedPointer d_ptr; private: diff --git a/VirtualReality/Widgets/qMRMLVirtualRealityView_p.h b/VirtualReality/Widgets/qMRMLVirtualRealityView_p.h index b224f43..44aef9e 100644 --- a/VirtualReality/Widgets/qMRMLVirtualRealityView_p.h +++ b/VirtualReality/Widgets/qMRMLVirtualRealityView_p.h @@ -40,6 +40,9 @@ #include #include +// VirtualReality Widgets includes +#include "qMRMLVirtualRealityHomeWidget.h" + // qMRML includes #include "qMRMLVirtualRealityView.h" @@ -115,6 +118,8 @@ public slots: double LastViewPosition[3]; QTimer VirtualRealityLoopTimer; + + qMRMLVirtualRealityHomeWidget* HomeWidget; }; #endif diff --git a/VirtualReality/qSlicerHomeVirtualWidget.cxx b/VirtualReality/qSlicerHomeVirtualWidget.cxx new file mode 100644 index 0000000..9d36104 --- /dev/null +++ b/VirtualReality/qSlicerHomeVirtualWidget.cxx @@ -0,0 +1,93 @@ +/*============================================================================== + + Program: 3D Slicer + + Portions (c) Copyright Brigham and Women's Hospital (BWH) All Rights Reserved. + + See COPYRIGHT.txt + or http://www.slicer.org/copyright/copyright.txt for details. + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +==============================================================================*/ + +#include +#include "qSlicerHomeVirtualReality.h" + +// Qt includes +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +//----------------------------------------------------------------------------- +// qSlicerHomeVirtualWidget Methods + +//----------------------------------------------------------------------------- +qSlicerHomeVirtualWidget::qSlicerHomeVirtualWidget(QWidget *parent) +{ + this->minimumHeight(650); + this->minimumWidth(980); + this->resize(dimensions::height, dimensions::width); + + //Create all the Qlabels + QLabel *flySpeedLabel = new QLabel("Fly Speed:", this); + QLabel *magnificationLabel = new QLabel("Magnification:", this); + QLabel *motionSenLabel = new QLabel("Motion Sensitivity:", this; + QLabel *lightingLabel = new QLabel("Lighting:", this); + + //Create all the push buttons + QPushButton *syncView = new QPushButton("Sync view to Reference View", this); + QPushButton *magnificationButton1 = new QPushButton("0.5x", this); + QPushButton *magnificationButton2 = new QPushButton("1x", this); + QPushButton *magnificationButton3 = new QPushButton("2x", this); + QPushButton *magnificationButton4 = new QPushButton("40x", this); + QPushButton *twoSidedLighting = new QPushButton("Two-sided Lighting", this); + QPushButton *backLighting = new QPushButton("Back Lighting", this); + + //create all sliders + QSlider *flySpeedSlider = new QSlider(Qt::Horizontal, this); + QSlider *motionSenSlider = new QSlider(Qt::Horizontal, this); + + //create the layouts for the UI + QFormLayout *menuLayout = new QFormLayout(this); + QHBoxLayout *magnificationButtonLayout = new QHBoxLayout(this); + QHBoxLayout *lightingButtonLayout = new QHBoxLayout(this); + + //place Qwidgets in appropriate layouts + magnificationButtonLayout->addWidget(magnificationButton1); + magnificationButtonLayout->addWidget(magnificationButton2); + magnificationButtonLayout->addWidget(magnificationButton3); + magnificationButtonLayout->addWidget(magnificationButton4); + lightingButtonLayout->addWidget(twoSidedLighting); + lightingButtonLayout->addWidget(backLighting); + + //set up the main form layout + menuLayout->addRow(flySpeedLabel, flySpeedSlider); + menuLayout->addRow(magnificationLabel, magnificationButtonLayout); + menuLayout->addRow(motionSenLabel, motionSenSlider); + menuLayout->addRow(lightingLabel, lightingButtonLayout); + + //spacing and positioning + menuLayout->setHorizontalSpacing(20); + menuLayout->setVerticleSpacing(99); +} +//--------------------------------------------------------------- +qSlicerHomeVirtualWidget::~qSlicerHomeVirtualWidget() +{ +}; \ No newline at end of file diff --git a/VirtualReality/qSlicerHomeVirtualWidget.h b/VirtualReality/qSlicerHomeVirtualWidget.h new file mode 100644 index 0000000..c27ba39 --- /dev/null +++ b/VirtualReality/qSlicerHomeVirtualWidget.h @@ -0,0 +1,43 @@ +/*============================================================================== + + Program: 3D Slicer + + Portions (c) Copyright Brigham and Women's Hospital (BWH) All Rights Reserved. + + See COPYRIGHT.txt + or http://www.slicer.org/copyright/copyright.txt for details. + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +==============================================================================*/ + +#ifndef __qSlicerHomeVirtualWidget_h +#define __qSlicerHomeVirtualWidget_h + +// SlicerQt includes +//Qt includes +#include +#include + +namespace dimensions +{ + const int height = 688; + const int width = 980; +} + +class qSlicerHomeVirtualWidget : public QWidget +{ + Q_OBJECT +public: + qSlicerHomeVirtualWidget(QWidget *parent = 0); + ~qSlicerHomeVirtualWidget(); +public slots: + +}; + + +#endif