Difference between revisions of "Documentation/Nightly/Developers/FAQ/Python Scripting"

From Slicer Wiki
Jump to: navigation, search
Line 87: Line 87:
 
<pre>
 
<pre>
 
def sceneModifiedCallback(caller, eventId):
 
def sceneModifiedCallback(caller, eventId):
   print "Scene modified"
+
   print("Scene modified")
   print "There are {0} nodes in the scene". format(slicer.mrmlScene.GetNumberOfNodes())
+
   print("There are {0} nodes in the scene". format(slicer.mrmlScene.GetNumberOfNodes()))
  
 
sceneModifiedObserverTag = slicer.mrmlScene.AddObserver(vtk.vtkCommand.ModifiedEvent, sceneModifiedCallback)
 
sceneModifiedObserverTag = slicer.mrmlScene.AddObserver(vtk.vtkCommand.ModifiedEvent, sceneModifiedCallback)
Line 98: Line 98:
 
@vtk.calldata_type(vtk.VTK_OBJECT)
 
@vtk.calldata_type(vtk.VTK_OBJECT)
 
def nodeAddedCallback(caller, eventId, callData):
 
def nodeAddedCallback(caller, eventId, callData):
   print "Node added"
+
   print("Node added")
   print "New node: {0}".format(callData.GetName())
+
   print("New node: {0}".format(callData.GetName()))
  
 
nodeAddedModifiedObserverTag = slicer.mrmlScene.AddObserver(slicer.vtkMRMLScene.NodeAddedEvent, nodeAddedCallback)
 
nodeAddedModifiedObserverTag = slicer.mrmlScene.AddObserver(slicer.vtkMRMLScene.NodeAddedEvent, nodeAddedCallback)
Line 112: Line 112:
 
     from functools import partial
 
     from functools import partial
 
     def nodeAddedCallback(self, caller, eventId, callData):
 
     def nodeAddedCallback(self, caller, eventId, callData):
       print "Node added"
+
       print("Node added")
       print "New node: {0}".format(callData.GetName())
+
       print("New node: {0}".format(callData.GetName()))
 
     self.nodeAddedCallback = partial(nodeAddedCallback, self)
 
     self.nodeAddedCallback = partial(nodeAddedCallback, self)
 
     self.nodeAddedCallback.CallDataType = vtk.VTK_OBJECT
 
     self.nodeAddedCallback.CallDataType = vtk.VTK_OBJECT
Line 140: Line 140:
 
   @vtk.calldata_type(vtk.VTK_OBJECT)
 
   @vtk.calldata_type(vtk.VTK_OBJECT)
 
   def nodeAddedCallback(self, caller, eventId, callData):
 
   def nodeAddedCallback(self, caller, eventId, callData):
     print "Node added"
+
     print("Node added")
     print "New node: {0}".format(callData.GetName())
+
     print("New node: {0}".format(callData.GetName()))
  
 
myObject = MyClass()
 
myObject = MyClass()

Revision as of 20:54, 26 August 2015

Home < Documentation < Nightly < Developers < FAQ < Python Scripting


For the latest Slicer documentation, visit the read-the-docs.


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.

See What is my 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:


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

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.

Visual debugging of Python modules in Slicer

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 (but currently this is not usable for scripted loadable module classes, only for simple helper classes, see Documentation/Labs/PythonObserverCallbacks page for details):

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)

[4]