Documentation/4.4/Developers/Python scripting
For the latest Slicer documentation, visit the read-the-docs. |
Contents
- 1 Background
- 2 Start Here for Scripted Module and Extension Development
- 3 Usage options
- 4 Script Repository
- 5 Developer FAQ: Python Scripting
- 5.1 How to systematically execute custom python code at startup ?
- 5.2 How to save an image/volume using python ?
- 5.3 How to assign a volume to a Slice view ?
- 5.4 How to access vtkRenderer in Slicer 3D view ?
- 5.5 Should I used 'old style' or 'new style' python classes in my scripted module
- 5.6 How to harden a transform ?
- 5.7 Where can I find example scripts?
- 5.8 How can I use a visual debugger for step-by-step debugging
- 5.9 Why can't I access my C++ Qt class from python
- 5.10 How can I access callData argument in a VTK object observer callback function
Background
This is an evolution of the python implementation in slicer3. Slicer's APIs are now natively wrapped in python.
Topics like plotting are still experimental in slicer4.
See this 2012 presentation on the state of python in slicer4.
See the python slicer4 tutorial for more examples.
Slicer Self Tests can be written in python, and provide a good source of examples for manipulating the data, logic, and gui of slicer.
Start Here for Scripted Module and Extension Development
An extensive tutorial and reference page was created for the Slicer/Python breakout session at the NA-MIC 2013 Summer Project Week.
Usage options
Python Interactor
Use the Window->Python Interactor (Control-3 on window/linux, Command-3 on mac) to bring up the Qt-based console with access to the vtk, Qt, and Slicer wrapped APIs.
Most python code can be installed and run from this window, but because it exists in the event driven Qt GUI environment, some operations like, like parallel processing or headless operation, are not easily supported.
Examples
Start slicer4 and bring up python console. Load a volume like this:
>>> slicer.util.loadVolume(slicer.app.slicerHome + "/share/MRML/Testing/TestData/fixed.nrrd")
Get the volume node for that volume:
>>> n = getNode('fixed')
You can use Tab to see lists of methods for a class instance.
Accessing Volume data as numpy array
You can easily inspect and manipulate volume data using numpy and related code. In slicer you can do this:
>>> a = array('fixed')
and a will be a pointer to the appropriate data (no data copying). Scalar volumes become three-dimensional arrays, while vector volumes become 4D, and tensor volumes are 5D. All arrays can be manipulated directly.
Running a CLI from Python
Here's an example to create a model from a volume using the Grayscale Model Maker
def grayModel(volumeNode): parameters = {} parameters["InputVolume"] = volumeNode.GetID() outModel = slicer.vtkMRMLModelNode() slicer.mrmlScene.AddNode( outModel ) parameters["OutputGeometry"] = outModel.GetID() grayMaker = slicer.modules.grayscalemodelmaker return (slicer.cli.run(grayMaker, None, parameters))
To try this, download the MRHead dataset from the Sample Data and paste the code into the python console and then run this:
v = getNode('MRHead') cliNode = grayModel(v)
Note that the CLI module runs in a background thread, so the call to grayModel will return right away. But the slicer.cli.run call returns a cliNode (an instance of vtkMRMLCommandLineModuleNode) which can be used to monitor the progress of the module.
Passing Fiducials to CLIs via a Python Script
import SampleData sampleDataLogic = SampleData.SampleDataLogic() head = sampleDataLogic.downloadMRHead() volumesLogic = slicer.modules.volumes.logic() headLabel = volumesLogic.CreateLabelVolume(slicer.mrmlScene, head, 'head-label') fiducialNode = slicer.vtkMRMLAnnotationFiducialNode() fiducialNode.SetFiducialWorldCoordinates((1,0,5)) fiducialNode.SetName('Seed Point') fiducialNode.Initialize(slicer.mrmlScene) fiducialsList = getNode('Fiducials List') params = {'inputVolume': head.GetID(), 'outputVolume': headLabel.GetID(), 'seed' : fiducialsList.GetID(), 'iterations' : 2} cliNode = slicer.cli.run(slicer.modules.simpleregiongrowingsegmentation, None, params , wait_for_completion=True)
Checking Status
In this example we create a simple callback that will be called whenever the cliNode is modified. The status will tell you if the nodes is Pending, Running, or Completed.
def printStatus(caller, event): print("Got a %s from a %s" % (event, caller.GetClassName())) if caller.IsA('vtkMRMLCommandLineModuleNode'): print("Status is %s" % caller.GetStatusString()) cliNode.AddObserver('ModifiedEvent', printStatus)
Get list of parameter names
The following script prints all the parameter names of a CLI parameter node:
cliModule = slicer.modules.grayscalemodelmaker n=cliModule.cliModuleLogic().CreateNode() for groupIndex in xrange(0,n.GetNumberOfParameterGroups()): for parameterIndex in xrange(0,n.GetNumberOfParametersInGroup(groupIndex)): print ' Parameter ({0}/{1}): {2}'.format(groupIndex, parameterIndex, n.GetParameterName(groupIndex, parameterIndex))
Accessing slice vtkRenderWindows from slice views
The example below shows how to get the rendered slice window.
lm = slicer.app.layoutManager() redWidget = lm.sliceWidget('Red') redView = redWidget.sliceView() wti = vtk.vtkWindowToImageFilter() wti.SetInput(redView.renderWindow()) wti.Update() v = vtk.vtkImageViewer() v.SetColorWindow(255) v.SetColorLevel(128) v.SetInputConnection(wti.GetOutputPort()) v.Render()
Script Repository
See ScriptRepository
Developer FAQ: Python Scripting
How to systematically execute custom python code at startup ?
Each time Slicer starts, it will look up for a file named .slicerrc.py
into your HOME folder.
How to save an image/volume using python ?
The module slicer.util
provides methods allowing to save either a node or an entire scene:
- saveNode
- saveScene
For more details see:
- https://github.com/Slicer/Slicer/blob/master/Base/Python/slicer/util.py#L229-267
- https://github.com/Slicer/Slicer/blob/master/Base/Python/slicer/tests/test_slicer_util_save.py
How to assign a volume to a Slice view ?
Assuming the MRHead
sample data has been loaded, you could do the following:
red_logic = slicer.app.layoutManager().sliceWidget("Red").sliceLogic() red_cn = red_logic.GetSliceCompositeNode() red_logic.GetSliceCompositeNode().SetBackgroundVolumeID(slicer.util.getNode('MRHead').GetID())
Discussion: http://slicer-devel.65872.n3.nabble.com/Assign-volumes-to-views-tt4028694.html
How to access vtkRenderer in Slicer 3D view ?
renderer = slicer.app.layoutManager().threeDWidget(0).threeDView().renderWindow().GetRenderers().GetFirstRenderer()
Should I used 'old style' or 'new style' python classes in my scripted module
When python classes have no superclass specified they are 'old style' as described here [1].
In general it doesn't matter for the classes in a scripted module, since they won't be subclassed either old or new style should be the same.
For other python code in slicer where you might be subclassing, it's better to use new style classes. See the class hierarchies in the EditorLib and the DICOMLib for examples.
How to harden a transform ?
>>> n = getNode('Bone') >>> logic = slicer.vtkSlicerTransformLogic() >>> logic.hardenTransform(n)
Discussion: http://slicer-devel.65872.n3.nabble.com/Scripting-hardened-transforms-tt4029456.html
Where can I find example scripts?
Have a look at Documentation/4.4/ScriptRepository.
How can I use a visual debugger for step-by-step debugging
Visual debugging (setting breakpoints, execute code step-by-step, view variables, stack, etc.) of Python scripted module is possible by using PyDev by using the Python debugger extension.
See detailed instructions at the Python debugger's extension page.
Why can't I access my C++ Qt class from python
- Python wrapping of a Qt class requires a Qt style constructor with QObject as argument (it can be defaulted to null though), which is public. If one of these are missing, python wrapping will fail for that class
- [Other reasons go here]
How can I access callData argument in a VTK object observer callback function
To get notification about an event emitted by a VTK object you can simply use the AddObserver method, for example:
def sceneModifiedCallback(caller, eventId): print "Scene modified" print "There are {0} nodes in the scene". format(slicer.mrmlScene.GetNumberOfNodes()) sceneModifiedObserverTag = slicer.mrmlScene.AddObserver(vtk.vtkCommand.ModifiedEvent, sceneModifiedCallback)
If an event also contains additional information as CallData then the type of this argument has to be specified as well, for example:
@vtk.calldata_type(vtk.VTK_OBJECT) def nodeAddedCallback(caller, eventId, callData): print "Node added" print "New node: {0}".format(callData.GetName()) nodeAddedModifiedObserverTag = slicer.mrmlScene.AddObserver(slicer.vtkMRMLScene.NodeAddedEvent, nodeAddedCallback)
Note: @vtk.calldata_type is a Python decorator, which modifies properties of a function that is declared right after the decorator. The decorator is defined in VTK (in Wrapping\Python\vtk\util\misc.py).
Usage from a class:
from slicer.util import VTKObservationMixin class MyClass(VTKObservationMixin): def __init__(self): VTKObservationMixin.__init__(self) self.addObserver(slicer.mrmlScene, slicer.vtkMRMLScene.NodeAddedEvent, self.nodeAddedCallback) @vtk.calldata_type(vtk.VTK_OBJECT) def nodeAddedCallback(self, caller, eventId, callData): print "Node added" print "New node: {0}".format(callData.GetName()) myObject = MyClass()
Allowed CallDataType values: VTK_STRING, VTK_OBJECT, VTK_INT, VTK_LONG, VTK_DOUBLE, VTK_FLOAT, "string0". See more information here: https://github.com/Kitware/VTK/blob/master/Wrapping/PythonCore/vtkPythonCommand.cxx
Note: VTKObservationMixin is a Python mix-in that allows adding a set of methods to a class by inheritance. VTKObservationMixin includes addObserver, hasObserver, observer, removeObserver, removeObservers methods, defined in Slicer (in Base\Python\slicer\util.py).