Skip to content

Instantly share code, notes, and snippets.

@cpinter
Last active January 20, 2022 17:55
Show Gist options
  • Save cpinter/dd367b2a1e06377436e5700231c70247 to your computer and use it in GitHub Desktop.
Save cpinter/dd367b2a1e06377436e5700231c70247 to your computer and use it in GitHub Desktop.
Custom app parameter node
__all__ = ['MyAppParameters',
'AbstractMyAppModuleWidget']
import logging
import vtk, qt, ctk, slicer
import Lib.MyAppParameters as Parameters
#
# AbstractModuleWidget
#
class AbstractMyAppModuleWidget():
def __init__(self):
pass
#------------------------------------------------------------------------------
def cleanup(self):
"""
Called when the application closes and the module widget is destroyed.
"""
self.removeObservers()
self.disconnect()
#------------------------------------------------------------------------------
def enter(self):
"""
Called each time the user opens this module.
"""
# Make sure parameter node exists and observed
self.observeParameterNode()
#------------------------------------------------------------------------------
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.unobserveParameterNode()
#------------------------------------------------------------------------------
def onSceneStartClose(self, caller, event):
"""
Called just before the scene is closed.
"""
# Parameter node will be reset, do not use it anymore
self.unobserveParameterNode()
#------------------------------------------------------------------------------
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.observeParameterNode()
#------------------------------------------------------------------------------
def observeParameterNode(self):
# Make sure parameter node exists and observed
self.addObserver(Parameters.instance.getParameterNode(), vtk.vtkCommand.ModifiedEvent, self.updateGUIFromParameterNode)
# Initial GUI update
self.updateGUIFromParameterNode()
#------------------------------------------------------------------------------
def unobserveParameterNode(self):
self.removeObserver(Parameters.instance.getParameterNode(), vtk.vtkCommand.ModifiedEvent, self.updateGUIFromParameterNode)
import logging
import os
from __main__ import vtk, qt, slicer # pylint: disable=no-name-in-module
# Make this instance easy to access:
#
# import MyAppLib.MyAppParameters as Parameters
# ...
# Parameters.instance.getParameterString(PARAM_NAME)
# Parameters.instance.getNodeReference(Parameters.NODE_REF)
#
instance = None
#
# Reference roles
# (reference names are same as the node names)
#
# Models
REF_CT_VOLUME = 'CtVolume'
REF_SEGMENTATION = 'Segmentation' # This reference is in the CT volume node
REF_NODE2 = 'Node2'
REF_NODE3 = 'Node3'
#
# Parameters
#
CURRENT_SEGMENT_ID = 'CurrentSegmentID' # In the segmentation REF_CT_VOLUME -> REF_SEGMENTATION
PARAMETER2 = 'Parameter2'
PARAMETER3 = 'Parameter3'
class MyAppParameters(object):
"""
Encapsulation of all core features and convenience functions related to
application-wide parameter node handling.
The MyAppLogic class still owns the parameter node (being subclass of
GuideletLogic), but this class encapsulates the related functionality for
easier usage and better code readability and maintainability. #TODO:!!! Edit
"""
def __init__(self, customAppModuleWidgetInstance):
"""
Constructor of class. Intializes variables with default values.
"""
self.customAppModuleWidgetInstance = customAppModuleWidgetInstance
# Pointer to the parameter node so that we have access to the old one before setting the new one
self.parameterNode = None
# Default parameters map
self.defaultParameters = {}
# moduleDir = os.path.dirname(slicer.modules.myapp.path)
# defaultSavePathOfMyApp = os.path.join(moduleDir, 'SavedScans')
self.defaultParameters[CURRENT_SEGMENT_ID] = ''
self.defaultParameters[PARAMETER2] = 128
self.defaultParameters[PARAMETER3] = 255
self.defaultParameters["ParticipantSelectionMode"] = 'New Participant'
self.defaultParameters["SelectedParticipantID"] = ''
self.defaultParameters["SelectedRecordingID"] = ''
def getParameterNode(self):
"""
Get parameter node.
"""
return self.customAppModuleWidgetInstance.logic.getParameterNode()
def setParameterNode(self, inputParameterNode):
"""
Set parameter node as main parameter node in the MyApp application.
- When importing a scene the parameter node from the scene is set
- When closing the scene, the parameter node is reset
- Handle observations of managed nodes (remove from old ones, add to new ones)
- Set default parameters if not specified in the given node
"""
logging.debug('MyAppParameters.setParameterNode')
if inputParameterNode == self.parameterNode:
return
# MyAppInstance = slicer.modules.MyAppWidget.guideletInstance
# # Remove observations from nodes referenced in the old parameter node
# if self.parameterNode and MyAppInstance:
# self.removeObserver(self.parameterNode, vtk.vtkCommand.ModifiedEvent, MyAppInstance.updateGUIFromMRML)
# Reset member variables
#TODO: If any
#
# Set parameter node member variable (so that we have access to the old one before setting the new one)
#
self.parameterNode = inputParameterNode
# Add observations on referenced nodes
# if self.parameterNode and MyAppInstance:
# self.addObserver(self.parameterNode, vtk.vtkCommand.ModifiedEvent, MyAppInstance.updateGUIFromMRML)
# Make sure parameter node is associated to this module
if self.parameterNode:
self.parameterNode.SetModuleName(self.customAppModuleWidgetInstance.moduleName)
#
# Set default parameters if missing
#
self.setDefaultParameters()
# Create nodes that are persistent in the scene and missing
#TODO: If any, like the basic transformation nodes or an SH folder
def setDefaultParameters(self, force=False):
"""
Set default parameters to the parameter node. The default parameters are stored in the map MyAppLogic.defaultParameters
:param bool force: Set default parameter even if the parameter is already set. False by default
"""
parameterNode = self.getParameterNode()
if not parameterNode:
logging.error('Failed to set default parameters due to missing parameter node')
return
existingParameterNames = parameterNode.GetParameterNames()
wasModified = parameterNode.StartModify() # Modify all properties in a single batch
for name, value in self.defaultParameters.items():
if not force and name in existingParameterNames:
continue
parameterNode.SetParameter(name, str(value))
parameterNode.EndModify(wasModified)
def getNodeReference(self, referenceRole):
"""
Get a given referenced node. Do not create it if does not exist but return None.
:param string referenceRole: The reference role for the referenced node
:return: Referenced node or None if does not exist
"""
parameterNode = self.getParameterNode()
if not parameterNode:
raise Exception('Failed to get parameter node')
nodeID = parameterNode.GetNodeReferenceID(referenceRole)
if nodeID in [None, '']:
return None
# Node is found, return it
node = slicer.mrmlScene.GetNodeByID(nodeID)
return node
def setNodeReference(self, referenceRole, node):
"""
Set a node reference.
:param string referenceRole: The reference role for the referenced node
:param vtkMRMLNode node: The node to reference
"""
if not node:
raise ValueError('Invalid node given to set reference "%s"' % referenceRole)
parameterNode = self.getParameterNode()
if not parameterNode:
raise Exception('Failed to get parameter node')
parameterNode.SetNodeReferenceID(referenceRole, node.GetID())
def setParameter(self, parameterName, parameterValue):
"""
Convenience function to set a parameter in the parameter node
:param string parameterName: Name of the parameter. Should be coming from a constant stored as member variable
:param string parameterValue: Value of the parameter. Any value is accepted and converted to string
"""
parameterNode = self.getParameterNode()
if not parameterNode:
raise Exception('Failed to set value %s to parameter named %s due to missing parameter node' % (str(parameterValue), parameterName))
parameterNode.SetParameter(parameterName, str(parameterValue))
def getParameterBool(self, parameterName):
"""
Convenience function to get a boolean parameter from the parameter node
:param string parameterName: Name of the parameter. Should be coming from a constant stored as member variable
:return bool: The parameter value if found, False otherwise
"""
parameterNode = self.getParameterNode()
if not parameterNode:
raise Exception('Failed to get value of parameter named %s due to missing parameter node' % (parameterName))
value = parameterNode.GetParameter(parameterName)
return (value == 'True')
def getParameterInt(self, parameterName):
"""
Convenience function to get an integer parameter from the parameter node
:param string parameterName: Name of the parameter. Should be coming from a constant stored as member variable
:return int: The parameter value if found, 0 otherwise
"""
parameterNode = self.getParameterNode()
if not parameterNode:
raise Exception('Failed to get value of parameter named %s due to missing parameter node' % (parameterName))
value = parameterNode.GetParameter(parameterName)
try:
return int(value)
except ValueError:
logging.error('Failed to convert value %s of parameter %s to integer. Returning zero' % (str(value), parameterName))
raise
def getParameterFloat(self, parameterName):
"""
Convenience function to get a floating point parameter from the parameter node
:param string parameterName: Name of the parameter. Should be coming from a constant stored as member variable
:return float: The parameter value if found, 0 otherwise
"""
parameterNode = self.getParameterNode()
if not parameterNode:
raise Exception('Failed to get value of parameter named %s due to missing parameter node' % (parameterName))
value = parameterNode.GetParameter(parameterName)
try:
return float(value)
except ValueError:
logging.error('Failed to convert value %s of parameter %s to floating point number. Returning zero' % (str(value), parameterName))
raise
def getParameterString(self, parameterName):
"""
Convenience function to get a string parameter from the parameter node
:param string parameterName: Name of the parameter. Should be coming from a constant stored as member variable
:return string: The parameter value if found, empty string otherwise
"""
parameterNode = self.getParameterNode()
if not parameterNode:
raise Exception('Failed to get value of parameter named %s due to missing parameter node' % (parameterName))
return parameterNode.GetParameter(parameterName)
@cpinter
Copy link
Author

cpinter commented Jan 19, 2022

These files go into MyAppProject\Modules\MyApp\Lib. Need to add them to MyAppProject\Modules\MyApp\CMakeLists.txt

@cpinter
Copy link
Author

cpinter commented Jan 20, 2022

Definition of modules in the custom app:
class SegmentationWidget(ScriptedLoadableModuleWidget, AbstractMyAppModuleWidget, VTKObservationMixin):

@cpinter
Copy link
Author

cpinter commented Jan 20, 2022

In the "controller" (or any other module that you consider the main one) module you need to set the main parameter node by creating the parameter class like this:
Parameters.instance = Parameters.MyAppParameters(self)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment