Last active
January 20, 2022 17:55
-
-
Save cpinter/dd367b2a1e06377436e5700231c70247 to your computer and use it in GitHub Desktop.
Custom app parameter node
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
__all__ = ['MyAppParameters', | |
'AbstractMyAppModuleWidget'] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) |
Definition of modules in the custom app:
class SegmentationWidget(ScriptedLoadableModuleWidget, AbstractMyAppModuleWidget, VTKObservationMixin):
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
These files go into
MyAppProject\Modules\MyApp\Lib
. Need to add them toMyAppProject\Modules\MyApp\CMakeLists.txt