Difference between revisions of "Documentation/Nightly/Developers/FAQ/Python Scripting"
Max.smolens (talk | contribs) |
Max.smolens (talk | contribs) |
||
Line 113: | Line 113: | ||
====Attach the debugger==== | ====Attach the debugger==== | ||
* Start Slicer. | * Start Slicer. | ||
− | * In Visual Studio, select ''Debug > Attach to Process'' to display the ''Attach to Process'' window | + | * In Visual Studio, select ''Debug > Attach to Process'' to display the ''Attach to Process'' window, then: |
** Choose ''Python remote (ptvsd)'' as the ''Transport''. | ** Choose ''Python remote (ptvsd)'' as the ''Transport''. | ||
** Enter "''tcp://slicer@localhost:5678''" as the ''Qualifier''. Here, "''slicer''" is the <tt>secret</tt> specified in <tt>enable_attach()</tt>. The default port, 5678, can be omitted. | ** Enter "''tcp://slicer@localhost:5678''" as the ''Qualifier''. Here, "''slicer''" is the <tt>secret</tt> specified in <tt>enable_attach()</tt>. The default port, 5678, can be omitted. |
Revision as of 20:58, 29 September 2015
Home < Documentation < Nightly < Developers < FAQ < Python Scripting
For the latest Slicer documentation, visit the read-the-docs. |
Contents
- 1 Python Scripting
- 1.1 How to systematically execute custom python code at startup ?
- 1.2 How to save an image/volume using python ?
- 1.3 How to assign a volume to a Slice view ?
- 1.4 How to access vtkRenderer in Slicer 3D view ?
- 1.5 How to center the 3D view on the scene ?
- 1.6 Should I use 'old style' or 'new style' python classes in my scripted module ?
- 1.7 How to harden a transform ?
- 1.8 Where can I find example scripts?
- 1.9 How can I use a visual debugger for step-by-step debugging
- 1.10 Why can't I access my C++ Qt class from python
- 1.11 How can I access callData argument in a VTK object observer callback function
- 1.12 How to run CLI module from Python?
- 1.13 How can I run slicer operations from a batch script?
- 1.14 How can I run Slicer on a headless compute node?
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()
How to center the 3D view on the scene ?
layoutManager = slicer.app.layoutManager() threeDWidget = layoutManager.threeDWidget(0) threeDView = threeDWidget.threeDView() threeDView.resetFocalPoint()
Should I use '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/Nightly/ScriptRepository.
How can I use a visual debugger for step-by-step debugging
Debugging using PyDev
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.
Debugging using Visual Studio
On Windows, Python Tools for Visual Studio (PTVS) enables debugging Python inside Visual Studio. Its remote debugging capability allows attaching the debugger to Slicer's embedded Python environment.
Setup and configuration
- Download the PTVS installer from https://github.com/Microsoft/PTVS. Note that as of October 2015 the latest version requires Visual Studio 2013 or Visual Studio 2015. PTVS installs as an extension to Visual Studio.
- Optional: configure Slicer's Python environment in Visual Studio. This enables IntelliSense specific to this Python environment.
- Select View > Other Windows > Python Environments to show the Python Environments window.
- Click "+ Custom"
- Add a description, enter the Prefix path to the python-install directory in your build tree, like C:\D\S4D\python-install, then click Auto Detect. The remaining fields should be populated automatically.
- Click Apply and make sure that the new environment is selected at the top of the window.
Install remote debugging server
To attach to a Slicer's Python instance it's first necessary to install ptvsd, the PTVS remote debugging server, in Slicer's Python environment:
- Install pip if necessary: ./Slicer --launch "C:\D\S4D\python-install\Scripts\easy_install.exe" pip
- Install ptvsd using pip: ./Slicer --launch "C:\D\S4D\python-install\Scripts\pip.exe" install ptvsd
Configure the remote debugging server
The PTVS page provides detailed remote debugging instructions. In brief, the steps are:
- In Visual Studio, open the script—the Slicer scripted module—to debug.
- Add the following code to the script to enable remote debugging:
import ptvsd ptvsd.enable_attach(secret='slicer') ptvsd.wait_for_attach()
Here, calling enable_attach() enables the remote debugging server. The secret parameter specifies a password that must be specified in Visual Studio when connecting to the debugger; it may be None.
Calling wait_for_attach() blocks Slicer until the debugger attaches.
Alternatively, this code could be added to ~/.slicerrc.py so that it's executed when Slicer starts.
Attach the debugger
- Start Slicer.
- In Visual Studio, select Debug > Attach to Process to display the Attach to Process window, then:
- Choose Python remote (ptvsd) as the Transport.
- Enter "tcp://slicer@localhost:5678" as the Qualifier. Here, "slicer" is the secret specified in enable_attach(). The default port, 5678, can be omitted.
- Press Enter or click Refresh. The Slicer process should appear in the list below. Select the process and click Attach.
Once the debugger attaches, you can use breakpoints, step through the code, and examine variables in the Watch window.
Notes
Debugging Slicer's C++ code and Python code simultaneously is possible by using separate instances of Visual Studio.
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 requires an extra step of creating the callback in the class __init__ function, as Python2 by default does some extra wrapping (http://stackoverflow.com/questions/9523370/adding-attributes-to-instance-methods-in-python):
class MyClass: def __init__(self): from functools import partial def nodeAddedCallback(self, caller, eventId, callData): print("Node added") print("New node: {0}".format(callData.GetName())) self.nodeAddedCallback = partial(nodeAddedCallback, self) self.nodeAddedCallback.CallDataType = vtk.VTK_OBJECT def registerCallbacks(self): self.nodeAddedModifiedObserverTag = slicer.mrmlScene.AddObserver(slicer.vtkMRMLScene.NodeAddedEvent, self.nodeAddedCallback) def unregisterCallbacks(self): slicer.mrmlScene.RemoveObserver(self.nodeAddedModifiedObserverTag) myObject = MyClass() myObject.registerCallbacks()
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
A simplified syntax is available by using a mix-in:
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()
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). For example of usage, see test_slicer_util_VTKObservationMixin.py
How to run CLI module from Python?
See here.
How can I run slicer operations from a batch script?
Slicer --no-main-window --python-script /tmp/test.py
Contents of /tmp/test.py
# use a slicer scripted module logic from SampleData import SampleDataLogic SampleDataLogic().downloadMRHead() head = slicer.util.getNode('MRHead') # use a vtk class threshold = vtk.vtkImageThreshold() threshold.SetInputData(head.GetImageData()) threshold.ThresholdBetween(100, 200) threshold.SetInValue(255) threshold.SetOutValue(0) # use a slicer-specific C++ class erode = slicer.vtkImageErode() erode.SetInputConnection(threshold.GetOutputPort()) erode.SetNeighborTo4() erode.Update() head.SetAndObserveImageData(erode.GetOutputDataObject(0)) slicer.util.saveNode(head, "/tmp/eroded.nrrd") exit()
How can I run Slicer on a headless compute node?
Many cluster nodes are installed with minimal linux systems that don't include X servers. X servers, particularly those with hardware acceleration traditionally needed to be installed with root privileges, making it impossible to run applications that rendered using X or OpenGL.
But there is a workaround which allows everything in slicer to work normally so you could even do headless rendering.
You can use a modern version of X that supports running a dummy framebuffer. This can be installed in user mode so you don't even need to have root on the system.
See [2] for details.
There's a thread here with more discussion: [3]
Here is a working example of the approach running on a headless compute node running CTK tests (which also use Qt and VTK)