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

From Slicer Wiki
Jump to: navigation, search
(Reference Developers/Tutorials/Debugging_Python_in_Visual_Studio page)
Line 83: Line 83:
 
On Windows, [https://github.com/Microsoft/PTVS 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.
 
On Windows, [https://github.com/Microsoft/PTVS 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====
+
See [[Documentation/{{documentation/version}}/Developers/Tutorials/Debugging_Python_in_Visual_Studio | Debugging Python in Visual Studio]] for details.
* 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 <tt>C:\D\S4D\python-install</tt>, 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 [https://pypi.python.org/pypi/ptvsd ptvsd], the PTVS remote debugging server, in Slicer's Python environment:
 
* Install pip if necessary: <tt>./Slicer --launch "C:\D\S4D\python-install\Scripts\easy_install.exe" pip</tt>
 
* Install ptvsd using pip: <tt>./Slicer --launch "C:\D\S4D\python-install\Scripts\pip.exe" install ptvsd</tt>
 
 
 
====Configure the remote debugging server====
 
The PTVS page provides detailed [https://github.com/Microsoft/PTVS/wiki/Cross-Platform-Remote-Debugging 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:
 
<pre>
 
import ptvsd
 
ptvsd.enable_attach(secret='slicer')
 
ptvsd.wait_for_attach()
 
</pre>
 
Here, calling <tt>enable_attach()</tt> enables the remote debugging server. The <tt>secret</tt> parameter specifies a password that must be specified in Visual Studio when connecting to the debugger; it may be <tt>None</tt>.
 
 
 
Calling <tt>wait_for_attach()</tt> blocks Slicer until the debugger attaches.
 
 
 
Alternatively, this code could be added to <tt>~/.slicerrc.py</tt> 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''" as the ''Qualifier''. Here, "''slicer''" should match the <tt>secret</tt> specified in <tt>enable_attach()</tt>.
 
** Press Enter or click ''Refresh''. The Slicer process should appear in the list below. Select the Slicer process and click ''Attach''.
 
 
 
Once the debugger attaches, you can use breakpoints, step through the code, and examine variables in the Watch window.
 
 
 
====Notes====
 
* It might be necessary to configure Windows Firewall to allow the Slicer executable to listen on port 5678.
 
* 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 ==
 
== Why can't I access my C++ Qt class from python ==

Revision as of 21:49, 29 September 2015

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


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

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.

Visual debugging of Python modules in Slicer

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.

See Debugging Python in Visual Studio for details.

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)

[4]