https://www.slicer.org/w/api.php?action=feedcontributions&user=Dzenanz&feedformat=atomSlicer Wiki - User contributions [en]2024-03-28T16:10:28ZUser contributionsMediaWiki 1.33.0https://www.slicer.org/w/index.php?title=Documentation/Nightly/Developers/Tutorials/MigrationGuide/Slicer&diff=59429Documentation/Nightly/Developers/Tutorials/MigrationGuide/Slicer2018-09-14T20:34:49Z<p>Dzenanz: ITK_LEGACY_REMOVE is now OFF</p>
<hr />
<div><noinclude>__TOC__</noinclude><br />
==Slicer backward incompatible changes==<br />
<br />
=== Slicer 4.9: ITK_LEGACY_REMOVE is now OFF ===<br />
In preparation to switch to ITK 5.0, we disable legacy functionality in ITK. This might affect some modules which rely on ITK. Take a look at [https://itk.org/migrationv4 ITK 4 migration guide] before [https://github.com/InsightSoftwareConsortium/ITK/blob/master/Documentation/ITK5MigrationGuide.md ITK 5 migration guide].<br />
<br />
=== Slicer 4.9: vtkMRMLPlotDataNode renamed to vtkMRMLPlotSeriesNode ===<br />
Plotting was improved in [https://github.com/Slicer/Slicer/commit/082edc40c this commit]<br />
<br />
'''Replace this:'''<br />
<br />
vtkMRMLPlotDataNode<br />
<br />
'''By this:'''<br />
vtkMRMLPlotSeriesNode<br />
<br />
=== Slicer 4.9: CMake: Module MIDAS not available ===<br />
<br />
The test infrastructure of your project should be updated to use [https://cmake.org/cmake/help/latest/module/ExternalData.html ExternalData] built-in CMake module<br />
instead of the specific <tt>MIDAS</tt> module.<br />
<br />
See EMSegment commit [http://viewvc.slicer.org/viewvc.cgi/Slicer3?view=revision&revision=17150 r17150] for an example of transition.<br />
<br />
This means that instead of using <tt>midas_add_test</tt> with the <tt>MIDAS{path/to/file.ext.md5}</tt><br />
syntax for addressing the test data, the function [https://cmake.org/cmake/help/latest/module/ExternalData.html#command:externaldata_add_test ExternalData_add_target] is used by<br />
specifying both <tt>DATA{path/to/file.ext}</tt> and a download target name.<br />
<br />
'''Replace this:'''<br />
<br />
midas_add_test(NAME test1 COMMAND ...)<br />
midas_add_test(NAME test2 COMMAND ...)<br />
<br />
'''By this:'''<br />
<br />
ExternalData_add_test(EMSegmentData NAME test1 COMMAND ...)<br />
ExternalData_add_test(EMSegmentData NAME test2 COMMAND ...)<br />
<br />
[...]<br />
<br />
ExternalData_add_target(EMSegmentData)<br />
<br />
<br />
A key difference with the former approaches is that instead of adding two tests (one named<br />
<tt><testName>_fetchData</tt> to downoad the data and one running the test command), only one<br />
test is added but a common download target is added at the end using [https://cmake.org/cmake/help/latest/module/ExternalData.html#command:externaldata_add_target ExternalData_add_target]<br />
function.<br />
<br />
This means that test data can now be downloaded in parallel (and cached) at build time instead<br />
of testing time.<br />
<br />
=== Slicer 4.9: CMake: Module SlicerMacroCheckExternalProjectDependency not available ===<br />
<br />
Since the module was removed in [http://viewvc.slicer.org/viewvc.cgi/Slicer4?view=revision&revision=26992 r26992], consider updating<br />
your build system to use CMake module <code>ExternalProjectDependency</code><br />
<br />
=== Slicer 4.9: CMake: Module SlicerMacroEmptyExternalProject not available ===<br />
<br />
Since the module was removed in [http://viewvc.slicer.org/viewvc.cgi/Slicer4?view=revision&revision=26991 r26991]<br />
<br />
'''Replace this:'''<br />
<br />
<pre><br />
include(SlicerMacroEmptyExternalProject)<br />
<br />
[...]<br />
<br />
SlicerMacroEmptyExternalProject("${proj}" "${${proj}_DEPENDENCIES}")<br />
</pre><br />
<br />
'''By this:'''<br />
<br />
<pre><br />
include(ExternalProjectDependency)<br />
<br />
[...]<br />
<br />
ExternalProject_Add_Empty(${proj} DEPENDS ${${proj}_DEPENDENCIES})<br />
</pre><br />
<br />
=== Slicer 4.9: CMake: Module SlicerBlockSetCMakeOSXVariables not available ===<br />
<br />
Since it was renamed to <tt>SlicerInitializeOSXVariables</tt> in [http://viewvc.slicer.org/viewvc.cgi/Slicer4?view=revision&revision=26982 r26982]<br />
<br />
'''Replace this:'''<br />
<br />
<pre><br />
include(SlicerBlockSetCMakeOSXVariables)<br />
</pre><br />
<br />
'''By this:'''<br />
<br />
<pre><br />
include(SlicerInitializeOSXVariables)<br />
</pre><br />
<br />
=== Slicer 4.9: Application: isRelease() function not available ===<br />
<br />
See [[#Slicer_4.8:_Application:_isRelease.28.29_function_not_available_or_deprecated]]<br />
<br />
=== Slicer 4.9: slicer.util.getNode() raises exception if node not found ===<br />
<br />
If slicer.util.getNode() is called and the node is not found then instead of just returning None (Slicer 4.8 behavior), the method now raises a MRMLNodeNotFoundException. This makes code debugging easier (the error is reported when it happens), and in general more consistent with Python conventions.<br />
<br />
How to update existing code:<br />
<br />
It is advisable to only use slicer.util.getNode in tests, or interactively in the Python console, as its behavior is somewhat unpredictable (it may either found a node by name or ID, and result of wildcard search is even less deterministic). In general, it is recommended to use the MRML scene's GetFirstNodeByName and GetNodeByID methods instead.<br />
<br />
'''Replace this:'''<br />
<br />
<pre><br />
n = slicer.util.getNode(nodeNameOrID)<br />
</pre><br />
<br />
'''By one of these:'''<br />
<br />
If node is to be found by name:<br />
<pre><br />
n = slicer.mrmlScene.GetFirstNodeByName(nodeName)<br />
</pre><br />
<br />
If node is to be found by ID:<br />
<pre><br />
n = slicer.mrmlScene.GetNodeByID(nodeID)<br />
</pre><br />
<br />
If node is to be found by name or ID (slower, less predictable, recommended for testing only):<br />
<br />
<pre><br />
try:<br />
n = slicer.util.getNode(nodeNameOrID)<br />
except slicer.util.MRMLNodeNotFoundException:<br />
n = None<br />
</pre><br />
<br />
More information: https://github.com/Slicer/Slicer/commit/b63484af1b1b413f35396f8f7efb73e870448bd4<br />
<br />
===Slicer 4.8: Application: isRelease() function not available or deprecated===<br />
<br />
<b>Error message similar to:</b><br />
<br />
Missing/deprecated qSlicerCoreApplication::isRelease()<br />
<br />
or<br />
<br />
Missing/deprecated slicer.app.isRelease()<br />
<br />
<br />
<b>Solution:</b><br />
<br />
Use <tt>qSlicerCoreApplication::releaseType() == "Stable"</tt><br />
<br />
<b>Summary:</b><br />
<br />
Prior to r26420, the variable <tt>Slicer_VERSION_TWEAK</tt> was used to check if a "stable release" was built. The variable value<br />
was set by updating the sources and defining the variable to an integer greater or equal to 0. In other word, if the variable<br />
evaluated to an empty string, a nighty or experimental build was being done, if it evaluated to an integer, a stable release build<br />
was being done.<br />
<br />
The approach had few issues:<br />
* the name of the variable was confusing<br />
* identifying a "stable release" only from a source tree revision was not enough. Indeed the environment defining a "release" is the one found on the build machines used to generate the installer.<br />
* nightly build are also considered as release<br />
<br />
To address this, the CMake variable <tt>Slicer_RELEASE_TYPE</tt> was introduced. As of 2017-10-04, it can be set to <tt>Experimental</tt>, <tt>Nightly</tt><br />
or <tt>Stable</tt> with <tt>Experimental</tt> being the value hard-coded in the source.<br />
<br />
Identifying a build as "stable" is now explicitly done by setting <tt>Slicer_RELEASE_TYPE</tt> to <tt>Stable</tt> at configure time.<br />
<br />
Also, since the concept of release types was introduced, the function <tt>isRelease()</tt> has been removed in favor of <tt>releaseType()</tt>.<br />
<br />
<b>References:</b><br />
<br />
https://github.com/Slicer/Slicer/pull/354<br />
<br />
===Slicer Python Module: modulewidget and others removed.===<br />
<br />
<b> Summary</b><br />
Python classes formerly in "slicer.moduledm", "slicer.modulelogic", "slicer.modulemrml"<br />
and "slicer.modulewidget" are now directly available in the slicer module.<br />
<br />
See example of change [https://github.com/QIICR/LongitudinalPETCT/pull/11 here].<br />
<br />
<b>Rational:</b><br />
<br />
See comments in commit messages referenced blow.<br />
<br />
<b>References:</b><br />
<br />
https://github.com/Slicer/Slicer/commit/628f83fe7a6f4e0710e306bcaf7c04b9e3e5e6bd<br />
<br />
https://github.com/Slicer/Slicer/commit/9cb5668fde1abc8f0430a91ca37fc29277ceeb4e<br />
<br />
===MRML: Slicer 4.6: Moved up vtkMRMLStorableNode in the MRML node hierarchy.===<br />
<br />
<b>Rational:</b><br />
<br />
vtkMRMLStorableNode is not a children of vtkMRMLTransformable node anymore,<br />
but directly a children of vtkMRMLNode.<br />
<br />
This allows making a node storable without requiring it to be also<br />
transformable. It is important for several node types (color maps, tables,<br />
etc), which require separate storage node but are not transformable.<br />
<br />
<b>References:</b><br />
* Changed introduced in [http://viewvc.slicer.org/viewvc.cgi/Slicer4?view=revision&revision=24891 r24891]<br />
<br />
<b>Error message similar to:</b><br />
<br />
/tmp/LongitudinalPETCT/MRML/vtkMRMLLongitudinalPETCTStudyNode.cxx: In member function ‘void vtkMRMLLongitudinalPETCTStudyNode::ObserveRegistrationTransform(bool)’:<br />
/tmp/LongitudinalPETCT/MRML/vtkMRMLLongitudinalPETCTStudyNode.cxx:478:28: error: ‘class vtkMRMLVolumePropertyNode’ has no member named ‘GetParentTransformNode’<br />
&& propNode->GetParentTransformNode()<br />
^<br />
/tmp/LongitudinalPETCT/MRML/vtkMRMLLongitudinalPETCTStudyNode.cxx:480:23: error: ‘class vtkMRMLVolumePropertyNode’ has no member named ‘SetAndObserveTransformNodeID’<br />
propNode->SetAndObserveTransformNodeID(<br />
^<br />
/tmp/LongitudinalPETCT/MRML/vtkMRMLLongitudinalPETCTStudyNode.cxx:503:23: error: ‘class vtkMRMLVolumePropertyNode’ has no member named ‘SetAndObserveTransformNodeID’<br />
propNode->SetAndObserveTransformNodeID(NULL);<br />
^<br />
<br />
<b>Solution:</b><br />
<br />
Removes lines and/or refactor code<br />
<br />
<br />
<br />
===MRML: Slicer 4.5: Introduction of vtkMRMLLabelMapVolumeNode===<br />
<br />
<b>Rational:</b><br />
<br />
Before <tt>vtkMRMLScalarVolumeNode</tt> was used for both scalar and label map<br />
volumes and the LabelMap custom MRML node attribute was used for<br />
distinguishing between them (0=scalar; 1=label map volume).<br />
<br />
This made conversion between labelmap/scalar volumes very easy but made<br />
it difficult to customize behavior, display, processing of segmentation<br />
information.<br />
<br />
Now a new <tt>vtkMRMLLabelMapVolumeNode</tt> class is used for storing segmentation<br />
information (still using <tt>vtkMRMLScalarVolume</tt> used as base class for backward<br />
compatibility; but in the future the base class may be changed to reflect<br />
that segmentation can be represented in various ways, not just as volumes).<br />
<br />
<b>Error message similar to:</b><br />
<br />
error: ‘class vtkMRMLScalarVolumeNode’ has no member named ‘SetLabelMap’<br />
outputVolumeNode->SetLabelMap(1);<br />
^<br />
<br />
<b>Solution (part1: down cast to <tt>vtkMRMLLabelMapVolumeNode</tt>, remove call to <tt>SetLabelMap</tt>)</b><br />
<br />
Replace lines like:<br />
<br />
vtkMRMLNode* outputNode = d->OutputLabelVolumeMRMLNodeComboBox->currentNode();<br />
vtkMRMLScalarVolumeNode* outputVolumeNode = vtkMRMLScalarVolumeNode::SafeDownCast(outputNode);<br />
[...]<br />
outputVolumeNode->SetLabelMap(1);<br />
<br />
<br />
with:<br />
<br />
vtkMRMLLabelMapVolumeNode* outputVolumeNode =<br />
vtkMRMLLabelMapVolumeNode::SafeDownCast(d->OutputLabelVolumeMRMLNodeComboBox->currentNode());<br />
[...]<br />
<br />
<br />
<b>Solution (part2: Update UI file):</b><br />
<br />
Replace lines like:<br />
<br />
<widget class="qMRMLNodeComboBox" name="InputLabelVolumeMRMLNodeComboBox"><br />
<property name="nodeTypes"><br />
<stringlist><br />
<string>vtkMRMLScalarVolumeNode</string><br />
</stringlist><br />
</property><br />
[...]<br />
</widget><br />
<br />
with:<br />
<br />
<widget class="qMRMLNodeComboBox" name="InputLabelVolumeMRMLNodeComboBox"><br />
<property name="nodeTypes"><br />
<stringlist><br />
<string>vtkMRMLLabelMapVolumeNode</string> <------------- Update Here<br />
</stringlist><br />
</property><br />
[...]<br />
</widget><br />
<br />
<br />
<b>Solution (part3: Update node selector configuration):</b><br />
<br />
Replace lines like:<br />
<br />
nodeSelector.setNodeTypes(QStringList("vtkMRMLScalarVolumeNode"));<br />
nodeSelector.addAttribute("vtkMRMLScalarVolumeNode", "LabelMap", "1");<br />
<br />
with:<br />
<br />
nodeSelector.setNodeTypes(QStringList("vtkMRMLLabelMapVolumeNode"));<br />
<br />
<br />
<b>References:</b><br />
* http://www.slicer.org/slicerWiki/index.php/Documentation/Labs/Segmentations#vtkMRMLLabelMapVolumeNode_integration<br />
* http://viewvc.slicer.org/viewvc.cgi/Slicer4?view=revision&revision=24291<br />
<br />
<br />
===CLI: Slicer 4.3: Add ITKFactoryRegistration library centralizing ITK IO factory registration===<br />
<br />
<b>Rational:</b><br />
<br />
Linking against <tt>ITKFactoryRegistration</tt> ensures that ITK IO factory are properly registered on all supported platforms.<br />
<br />
<b>Error message similar to:</b><br />
<br />
Undefined symbols for architecture x86_64:<br />
"itk::itkFactoryRegistration()", referenced from:<br />
_main in ImageMakerTest.cxx.o<br />
ld: symbol(s) not found for architecture x86_64<br />
<br />
<b>Solution:</b><br />
<br />
Replace lines like:<br />
<br />
target_link_libraries(${CLP}Test ${CLP}Lib)<br />
<br />
with:<br />
<br />
target_link_libraries(${CLP}Test ${CLP}Lib ${SlicerExecutionModel_EXTRA_EXECUTABLE_TARGET_LIBRARIES})<br />
<br />
<b>References:</b><br />
<br />
* http://viewvc.slicer.org/viewvc.cgi/Slicer4?view=revision&revision=21592<br />
* https://issues.slicer.org/view.php?id=2813</div>Dzenanzhttps://www.slicer.org/w/index.php?title=Documentation/Nightly/ScriptRepository&diff=53815Documentation/Nightly/ScriptRepository2017-08-02T15:16:26Z<p>Dzenanz: How to get path and filename of a loaded DICOM volume?</p>
<hr />
<div><noinclude>{{documentation/versioncheck}}</noinclude><br />
__TOC__<br />
<br />
<br />
=Community-contributed modules=<br />
<br />
Usage: save the .py file to a directory, add the directory to the additional module paths in the Slicer application settings (choose in the menu: Edit / Application settings, click Modules, click >> next to Additional module paths, click Add, and choose the .py file's location).<br />
<br />
==Filters==<br />
* [https://raw.github.com/pieper/VolumeMasker/master/VolumeMasker.py VolumeMasker.py]: Update a target volume with the results of setting all input volume voxels to 0 except for those that correspond to a selected label value in an input label map (Used for example in the volume rendering in [https://www.youtube.com/watch?v=dfu2gugHLHs this video).<br />
<br />
==DICOM==<br />
* [https://gist.github.com/pieper/6186477 dicom header browser] to easily scroll through dicom files using dcmdump.<br />
* [https://subversion.assembla.com/svn/slicerrt/trunk/SlicerRt/src/BatchProcessing SlicerRT batch processing] to batch convert RT structure sets to labelmap NRRD files.<br />
<br />
==Informatics==<br />
* [https://subversion.assembla.com/svn/slicerrt/trunk/SlicerRt/sandbox/MarkupsInfoModule/MarkupsInfo.py MarkupsInfo.py]: Compute the total length between all the points of a markup list.<br />
* [https://subversion.assembla.com/svn/slicerrt/trunk/SlicerRt/sandbox/LineProfile/LineProfile.py LineProfile.py]: Compute intensity profile in a volume along a line.<br />
<br />
=Community-contributed examples=<br />
<br />
Usage: Copy-paste the shown code lines or linked .py file contents into Python console in Slicer.<br />
<br />
==Capture==<br />
* Capture the full Slicer screen and save it into a file<br />
img = qt.QPixmap.grabWidget(slicer.util.mainWindow()).toImage()<br />
img.save('c:/tmp/test.png')<br />
* Capture all the views save it into a file:<br />
<pre><br />
import ScreenCapture<br />
cap = ScreenCapture.ScreenCaptureLogic()<br />
cap.showViewControllers(False)<br />
cap.captureImageFromView(None,'c:/tmp/test.png')<br />
cap.showViewControllers(True)<br />
</pre><br />
* Capture a single view:<br />
<pre><br />
viewNodeID = 'vtkMRMLViewNode1'<br />
import ScreenCapture<br />
cap = ScreenCapture.ScreenCaptureLogic()<br />
view = logic.viewFromNode(slicer.mrmlScene.GetNodeByID(viewNodeID))<br />
cap.captureImageFromView(view,'c:/tmp/test.png')<br />
</pre><br />
Common values for viewNodeID: vtkMRMLSliceNodeRed, vtkMRMLSliceNodeYellow, vtkMRMLSliceNodeGreen, vtkMRMLViewNode1, vtkMRMLViewNode2. <br />
The ScreenCapture module can also create video animations of rotating views, slice sweeps, etc.<br />
<br />
==Launching Slicer==<br />
* How to open an .mrb file with Slicer at the command line?<br />
Slicer.exe --python-code "slicer.util.loadScene( 'f:/2013-08-23-Scene.mrb' )"<br />
* How to run a script in the Slicer environment in batch mode (without showing any graphical user interface)?<br />
Slicer.exe --python-code "doSomething; doSomethingElse; etc." --testing --no-splash --no-main-window<br />
<br />
==Load volume from file==<br />
When loading a volume from file, it is recommended to set returnNode=True to retrieve the loaded volume node.<br />
<pre><br />
[success, loadedVolumeNode] = slicer.util.loadVolume('c:/Users/abc/Documents/MRHead.nrrd', returnNode=True)<br />
</pre><br />
<br />
* Get a MRML node in the scene based on the node name and call methods of that object. For the MRHead sample data:<br />
vol=slicer.util.getNode('MR*')<br />
vol.GetImageData().GetDimensions()<br />
<br />
==DICOM==<br />
=== How to access tags of DICOM images imported into Slicer? For example, to print the first patient's first study's first series' "0020,0032" field:===<br />
db=slicer.dicomDatabase<br />
patientList=db.patients()<br />
studyList=db.studiesForPatient(patientList[0])<br />
seriesList=db.seriesForStudy(studyList[0])<br />
fileList=db.filesForSeries(seriesList[0])<br />
print db.fileValue(fileList[0],'0020,0032')<br />
<br />
=== How to access tag of a volume loaded from DICOM? For example, get the patient position stored in a volume:===<br />
volumeName='2: ENT IMRT'<br />
n=slicer.util.getNode(volumeName)<br />
instUids=n.GetAttribute('DICOM.instanceUIDs').split()<br />
filename=slicer.dicomDatabase.fileForInstance(instUids[0])<br />
print slicer.dicomDatabase.fileValue(filename,'0018,5100')<br />
<br />
=== How to access tag of an item in the Subject Hierachy tree? For example, get the content time tag of a structure set:===<br />
rtStructName = '3: RTSTRUCT: PROS'<br />
rtStructNode = slicer.util.getNode(rtStructName)<br />
shNode = slicer.vtkMRMLSubjectHierarchyNode.GetSubjectHierarchyNode(slicer.mrmlScene)<br />
rtStructShItemID = shNode.GetItemByDataNode(rtStructNode)<br />
ctSliceInstanceUids = shNode.GetItemAttribute(rtStructShItemID, 'DICOM.ReferencedInstanceUIDs').split()<br />
filename = slicer.dicomDatabase.fileForInstance(ctSliceInstanceUids[0])<br />
print slicer.dicomDatabase.fileValue(filename,'0008,0033')<br />
<br />
=== How to get path and filename of a loaded DICOM volume?===<br />
def PathFromNode(self, node):<br />
storageNode=node.GetStorageNode()<br />
if storageNode is not None: # loaded via drag-drop<br />
filepath=storageNode.GetFullNameFromFileName()<br />
else: # loaded via DICOM browser<br />
instanceUIDs=node.GetAttribute('DICOM.instanceUIDs').split()<br />
filepath=slicer.dicomDatabase.fileForInstance(instUids[0])<br />
return filepath<br />
<br />
# example invocation<br />
node=slicer.util.getFirstNodeByName('volume1')<br />
path=self.PathFromNode(node)<br />
print "DICOM path="+path<br />
<br />
=== How can I convert DICOM to NRRD on the command line?===<br />
<br />
/Applications/Slicer-4.6.2.app/Contents/MacOS/Slicer --no-main-window --python-code "node=slicer.util.loadVolume('/tmp/series/im0.dcm', returnNode=True)[1]; slicer.util.saveNode(node, '/tmp/output.nrrd'); exit()"<br />
<br />
The same can be done on windows by using the top level Slicer.exe. Be sure to use forward slashes in the pathnames within quotes on the command line.<br />
<br />
==Toolbar functions==<br />
* How to turn on slice intersections in the crosshair menu on the toolbar:<br />
viewNodes = slicer.mrmlScene.GetNodesByClass('vtkMRMLSliceCompositeNode')<br />
viewNodes.UnRegister(slicer.mrmlScene)<br />
viewNodes.InitTraversal()<br />
viewNode = viewNodes.GetNextItemAsObject()<br />
while viewNode:<br />
viewNode.SetSliceIntersectionVisibility(1)<br />
viewNode = viewNodes.GetNextItemAsObject()<br />
<br />
How to find similar functions? For this one I searched for "slice intersections" text in the whole slicer source code, found that the function is implemented in Base\QTGUI\qSlicerViewersToolBar.cxx, then translated the qSlicerViewersToolBarPrivate::setSliceIntersectionVisible(bool visible) method to Python.<br />
<br />
==Manipulating objects in the slice viewer==<br />
* How to define/edit a circular region of interest in a slice viewer?<br />
<br />
Drop two markup points on a slice view and copy-paste the code below into the Python console. After this, as you move the markups you’ll see a circle following the markups.<br />
<br />
<pre><br />
# Update the sphere from the fiducial points<br />
def UpdateSphere(param1, param2): <br />
import math<br />
centerPointCoord = [0.0, 0.0, 0.0]<br />
markups.GetNthFiducialPosition(0,centerPointCoord)<br />
circumferencePointCoord = [0.0, 0.0, 0.0]<br />
markups.GetNthFiducialPosition(1,circumferencePointCoord)<br />
sphere.SetCenter(centerPointCoord)<br />
radius=math.sqrt((centerPointCoord[0]-circumferencePointCoord[0])**2+(centerPointCoord[1]-circumferencePointCoord[1])**2+(centerPointCoord[2]-circumferencePointCoord[2])**2)<br />
sphere.SetRadius(radius)<br />
sphere.SetPhiResolution(30)<br />
sphere.SetThetaResolution(30)<br />
sphere.Update()<br />
<br />
# Get markup node from scene<br />
markups=slicer.util.getNode('F')<br />
sphere = vtk.vtkSphereSource()<br />
UpdateSphere(0,0)<br />
<br />
# Create model node and add to scene<br />
modelsLogic = slicer.modules.models.logic()<br />
model = modelsLogic.AddModel(sphere.GetOutput())<br />
model.GetDisplayNode().SetSliceIntersectionVisibility(True)<br />
model.GetDisplayNode().SetSliceIntersectionThickness(3)<br />
model.GetDisplayNode().SetColor(1,1,0)<br />
<br />
# Call UpdateSphere whenever the fiducials are changed<br />
markups.AddObserver("ModifiedEvent", UpdateSphere, 2)<br />
</pre><br />
<br />
== Switching to markup fiducial placement mode ==<br />
<br />
To activate a fiducial placement mode, both interaction mode has to be set and a fiducial node has to be selected:<br />
<br />
<pre><br />
interactionNode = slicer.app.applicationLogic().GetInteractionNode()<br />
selectionNode = slicer.app.applicationLogic().GetSelectionNode()<br />
selectionNode.SetReferenceActivePlaceNodeClassName("vtkMRMLMarkupsFiducialNode")<br />
fiducialNode = slicer.vtkMRMLMarkupsFiducialNode()<br />
slicer.mrmlScene.AddNode(fiducialNode)<br />
fiducialNode.CreateDefaultDisplayNodes() <br />
selectionNode.SetActivePlaceNodeID(fiducialNode.GetID())<br />
interactionNode.SetCurrentInteractionMode(interactionNode.Place)<br />
</pre><br />
<br />
== Show a context menu when a markup point is clicked in a slice or 3D view ==<br />
<br />
<pre><br />
<br />
# Example actions to perform<br />
<br />
def action1():<br />
print('Action1 on markup '+str(slicer.clickedMarkupIndex))<br />
<br />
def action2():<br />
print('Action2 on markup '+str(slicer.clickedMarkupIndex))<br />
<br />
def action3():<br />
print('Action3 on markup '+str(slicer.clickedMarkupIndex))<br />
<br />
# Clicked markup index is saved here to let the action<br />
# know which markup needs to be manipulated.<br />
slicer.clickedMarkupIndex = -1<br />
<br />
# Create a simple menu<br />
<br />
menu = qt.QMenu()<br />
a1 = qt.QAction("Test", slicer.util.mainWindow())<br />
a1.connect('triggered()', action1)<br />
menu.addAction(a1)<br />
a2 = qt.QAction("Action", slicer.util.mainWindow())<br />
a2.connect('triggered()', action1)<br />
menu.addAction(a2)<br />
a3 = qt.QAction("Here", slicer.util.mainWindow())<br />
a3.connect('triggered()', action1)<br />
menu.addAction(a3)<br />
<br />
# Add observer to a markup fiducial list<br />
<br />
@vtk.calldata_type(vtk.VTK_INT)<br />
def markupClickedCallback(caller, eventId, callData):<br />
slicer.clickedMarkupIndex = callData<br />
print('Open menu on markup '+str(slicer.clickedMarkupIndex))<br />
menu.move(qt.QCursor.pos())<br />
menu.show()<br />
<br />
markupsNode = getNode('F')<br />
observerTag = markupsNode.AddObserver(slicer.vtkMRMLMarkupsNode.PointClickedEvent, markupClickedCallback)<br />
<br />
</pre><br />
<br />
== Add a texture mapped plane to the scene as a model ==<br />
Note that model textures are not exposed in the GUI and are not saved in the scene<br />
<pre><br />
# use dummy image data here<br />
e = vtk.vtkImageEllipsoidSource()<br />
<br />
scene = slicer.mrmlScene<br />
<br />
# Create model node<br />
model = slicer.vtkMRMLModelNode()<br />
model.SetScene(scene)<br />
model.SetName(scene.GenerateUniqueName("2DImageModel"))<br />
<br />
planeSource = vtk.vtkPlaneSource()<br />
model.SetAndObservePolyData(planeSource.GetOutput())<br />
<br />
# Create display node<br />
modelDisplay = slicer.vtkMRMLModelDisplayNode()<br />
modelDisplay.SetColor(1,1,0) # yellow<br />
modelDisplay.SetBackfaceCulling(0)<br />
modelDisplay.SetScene(scene)<br />
scene.AddNode(modelDisplay)<br />
model.SetAndObserveDisplayNodeID(modelDisplay.GetID())<br />
<br />
# Add to scene<br />
modelDisplay.SetAndObserveTextureImageData(e.GetOutput())<br />
scene.AddNode(model) <br />
<br />
<br />
transform = slicer.vtkMRMLLinearTransformNode()<br />
scene.AddNode(transform) <br />
model.SetAndObserveTransformNodeID(transform.GetID())<br />
<br />
vTransform = vtk.vtkTransform()<br />
vTransform.Scale(50,50,50)<br />
vTransform.RotateX(30)<br />
transform.SetAndObserveMatrixTransformToParent(vTransform.GetMatrix())<br />
</pre><br />
<br />
<br />
== Export entire scene as VRML ==<br />
<br />
Save all surface meshes displayed in the scene (models, markups, etc). Solid colors and coloring by scalar is preserved. Textures are not supported.<br />
<br />
<pre><br />
exporter = vtk.vtkVRMLExporter()<br />
exporter.SetRenderWindow(slicer.app.layoutManager().threeDWidget(0).threeDView().renderWindow())<br />
exporter.SetFileName('C:/tmp/something.wrl')<br />
exporter.Write()<br />
</pre><br />
<br />
== Export model to Blender, including color by scalar ==<br />
<br />
<pre><br />
modelNode = getNode("Model")<br />
plyFilePath = "c:/tmp/model.ply"<br />
<br />
modelDisplayNode = modelNode.GetDisplayNode()<br />
triangles = vtk.vtkTriangleFilter()<br />
triangles.SetInputConnection(modelDisplayNode.GetOutputPolyDataConnection())<br />
<br />
plyWriter = vtk.vtkPLYWriter()<br />
plyWriter.SetInputConnection(triangles.GetOutputPort())<br />
lut = vtk.vtkLookupTable()<br />
lut.DeepCopy(modelDisplayNode.GetColorNode().GetLookupTable())<br />
lut.SetRange(modelDisplayNode.GetScalarRange())<br />
plyWriter.SetLookupTable(lut)<br />
plyWriter.SetArrayName(modelDisplayNode.GetActiveScalarName())<br />
<br />
plyWriter.SetFileName(plyFilePath)<br />
plyWriter.Write()<br />
</pre><br />
<br />
== Export a fiber tracts to Blender, including color ==<br />
<br />
<pre><br />
lineDisplayNode = getNode("*LineDisplay*")<br />
plyFilePath = "/tmp/fibers.ply"<br />
<br />
tuber = vtk.vtkTubeFilter()<br />
tuber.SetInputData(lineDisplayNode.GetOutputPolyData())<br />
tuber.Update()<br />
tubes = tuber.GetOutput()<br />
scalars = tubes.GetPointData().GetArray(0)<br />
scalars.SetName("scalars")<br />
<br />
triangles = vtk.vtkTriangleFilter()<br />
triangles.SetInputData(tubes)<br />
<br />
colorNode = lineDisplayNode.GetColorNode()<br />
lookupTable = vtk.vtkLookupTable()<br />
lookupTable.DeepCopy(colorNode.GetLookupTable())<br />
lookupTable.SetTableRange(0,1)<br />
<br />
plyWriter = vtk.vtkPLYWriter()<br />
plyWriter.SetInputData(triangles.GetOutput())<br />
plyWriter.SetLookupTable(lookupTable)<br />
plyWriter.SetArrayName("scalars")<br />
<br />
plyWriter.SetFileName(plyFilePath)<br />
plyWriter.Write()<br />
</pre><br />
<br />
== Clone a volume ==<br />
This example shows how to clone the MRHead sample volume, including its pixel data and display settings.<br />
<pre><br />
sourceVolumeNode = slicer.util.getNode('MRHead')<br />
volumesLogic = slicer.modules.volumes.logic()<br />
clonedVolumeNode = volumesLogic.CloneVolume(slicer.mrmlScene, sourceVolumeNode, 'Cloned volume')<br />
</pre><br />
<br />
== Create a new volume ==<br />
This example shows how to create a new empty volume.<br />
<pre><br />
imageSize=[512, 512, 512]<br />
imageSpacing=[1.0, 1.0, 1.0]<br />
voxelType=vtk.VTK_UNSIGNED_CHAR<br />
# Create an empty image volume<br />
imageData=vtk.vtkImageData()<br />
imageData.SetDimensions(imageSize)<br />
imageData.AllocateScalars(voxelType, 1)<br />
thresholder=vtk.vtkImageThreshold()<br />
thresholder.SetInputData(imageData)<br />
thresholder.SetInValue(0)<br />
thresholder.SetOutValue(0)<br />
# Create volume node<br />
volumeNode=slicer.vtkMRMLScalarVolumeNode()<br />
volumeNode.SetSpacing(imageSpacing)<br />
volumeNode.SetImageDataConnection(thresholder.GetOutputPort())<br />
# Add volume to scene<br />
slicer.mrmlScene.AddNode(volumeNode)<br />
displayNode=slicer.vtkMRMLScalarVolumeDisplayNode()<br />
slicer.mrmlScene.AddNode(displayNode)<br />
colorNode = slicer.util.getNode('Grey')<br />
displayNode.SetAndObserveColorNodeID(colorNode.GetID())<br />
volumeNode.SetAndObserveDisplayNodeID(displayNode.GetID())<br />
volumeNode.CreateDefaultStorageNode()<br />
</pre><br />
<br />
== Modify voxels in a volume ==<br />
<br />
Typically the fastest and simplest way of modifying voxels is by using numpy operators. Voxels can be retrieved in a numpy array using the `array` method and modified using standard numpy methods. For example, threshold a volume:<br />
<br />
<pre><br />
nodeName = 'MRHead'<br />
thresholdValue = 100<br />
voxelArray = array(nodeName) # get voxels as numpy array<br />
voxelArray[voxelArray < thresholdValue] = 0 # modify voxel values<br />
getNode(nodeName).Modified() # at the end of all processing, notify Slicer that the image modification is completed<br />
</pre><br />
<br />
This example shows how to change voxels values of the MRHead sample volume.<br />
The values will be computed by function f(r,a,s,) = (r-10)*(r-10)+(a+15)*(a+15)+s*s.<br />
<pre><br />
volumeNode=slicer.util.getNode('MRHead')<br />
ijkToRas = vtk.vtkMatrix4x4()<br />
volumeNode.GetIJKToRASMatrix(ijkToRas)<br />
imageData=volumeNode.GetImageData()<br />
extent = imageData.GetExtent()<br />
for k in xrange(extent[4], extent[5]+1):<br />
for j in xrange(extent[2], extent[3]+1):<br />
for i in xrange(extent[0], extent[1]+1):<br />
position_Ijk=[i, j, k, 1]<br />
position_Ras=ijkToRas.MultiplyPoint(position_Ijk)<br />
r=position_Ras[0]<br />
a=position_Ras[1]<br />
s=position_Ras[2] <br />
functionValue=(r-10)*(r-10)+(a+15)*(a+15)+s*s<br />
imageData.SetScalarComponentFromDouble(i,j,k,0,functionValue)<br />
imageData.SetScalarComponentFromFloat(distortionVectorPosition_Ijk[0], distortionVectorPosition_Ijk[1], distortionVectorPosition_Ijk[2], 0, fillValue)<br />
imageData.Modified()<br />
</pre><br />
<br />
== Access values in a DTI tensor volume ==<br />
This example shows how to access individual tensors at the voxel level.<br />
<br />
First load your DWI volume and estimate tensors to produce a DTI volume called ‘Output DTI Volume’<br />
<br />
Then open the python window: View->Python interactor<br />
<br />
Use this command to access tensors through numpy:<br />
<br />
<pre><br />
tensors = array('Output DTI Volume')<br />
</pre><br />
<br />
Type the following code into the Python window to access all tensor components using vtk commands:<br />
<br />
<pre><br />
volumeNode=slicer.util.getNode('Output DTI Volume')<br />
imageData=volumeNode.GetImageData()<br />
tensors = imageData.GetPointData().GetTensors()<br />
extent = imageData.GetExtent()<br />
idx = 0<br />
for k in xrange(extent[4], extent[5]+1):<br />
for j in xrange(extent[2], extent[3]+1):<br />
for i in xrange(extent[0], extent[1]+1):<br />
tensors.GetTuple9(idx)<br />
idx += 1<br />
</pre><br />
<br />
== Change window/level (brightness/contrast) or colormap of a volume ==<br />
This example shows how to change window/level of the MRHead sample volume.<br />
<pre><br />
volumeNode = getNode('MRHead')<br />
displayNode = volumeNode.GetDisplayNode()<br />
displayNode.AutoWindowLevelOff()<br />
displayNode.SetWindow(50)<br />
displayNode.SetLevel(100)<br />
</pre><br />
<br />
Change color mapping from grayscale to rainbow:<br />
<pre><br />
displayNode.SetAndObserveColorNodeID('vtkMRMLColorTableNodeRainbow')<br />
</pre><br />
<br />
== Manipulate a Slice View ==<br />
<br />
<pre><br />
lm = slicer.app.layoutManager()<br />
red = lm.sliceWidget('Red')<br />
redLogic = red.sliceLogic()<br />
# Print current slice offset position<br />
print redLogic.GetSliceOffset()<br />
# Change slice position<br />
redLogic.SetSliceOffset(20)<br />
</pre><br />
<br />
== Save a series of images from a Slice View ==<br />
<br />
You can use ScreenCapture module to capture series of images. To do it programmatically, save the following into a file such as '/tmp/record.py' and then in the slicer python console type "execfile('/tmp/record.py')"<br />
<br />
<pre><br />
layoutName = 'Green'<br />
imagePathPattern = '/tmp/image-%03d.png'<br />
steps = 10<br />
<br />
widget = slicer.app.layoutManager().sliceWidget(layoutName)<br />
view = widget.sliceView()<br />
logic = widget.sliceLogic()<br />
bounds = [0,]*6<br />
logic.GetSliceBounds(bounds)<br />
<br />
for step in range(steps):<br />
offset = bounds[4] + step/(1.*steps) * (bounds[5]-bounds[4])<br />
logic.SetSliceOffset(offset)<br />
view.forceRender()<br />
image = qt.QPixmap.grabWidget(view).toImage()<br />
image.save(imagePathPattern % step)<br />
</pre><br />
<br />
== Save the scene into a new directory ==<br />
<br />
<pre><br />
# Create a new directory where the scene will be saved into<br />
import time<br />
sceneSaveDirectory = slicer.app.temporaryPath + "/saved-scene-" + time.strftime("%Y%m%d-%H%M%S")<br />
if not os.access(sceneSaveDirectory, os.F_OK):<br />
os.makedirs(sceneSaveDirectory)<br />
<br />
# Save the scene<br />
if slicer.app.applicationLogic().SaveSceneToSlicerDataBundleDirectory(sceneSaveDirectory, None):<br />
logging.info("Scene saved to: {0}".format(sceneSaveDirectory))<br />
else:<br />
logging.error("Scene saving failed") <br />
</pre><br />
<br />
== Save the scene into a single MRB file ==<br />
<pre><br />
# Generate file name<br />
import time<br />
sceneSaveFilename = slicer.app.temporaryPath + "/saved-scene-" + time.strftime("%Y%m%d-%H%M%S") + ".mrb"<br />
<br />
# Save scene<br />
if slicer.util.saveScene(sceneSaveFilename):<br />
logging.info("Scene saved to: {0}".format(sceneSaveFilename))<br />
else:<br />
logging.error("Scene saving failed") <br />
</pre><br />
<br />
== Show a volume in the Slice Views ==<br />
<br />
<pre><br />
volumeNode = slicer.util.getNode('YourVolumeNode')<br />
applicationLogic = slicer.app.applicationLogic()<br />
selectionNode = applicationLogic.GetSelectionNode()<br />
selectionNode.SetSecondaryVolumeID(volumeNode.GetID())<br />
applicationLogic.PropagateForegroundVolumeSelection(0) <br />
</pre><br />
<br />
or<br />
<br />
<pre><br />
n = slicer.util.getNode('YourVolumeNode')<br />
for color in ['Red', 'Yellow', 'Green']:<br />
slicer.app.layoutManager().sliceWidget(color).sliceLogic().GetSliceCompositeNode().SetForegroundVolumeID(n.GetID())<br />
</pre><br />
<br />
== Change opacity of foreground volume in the Slice Views ==<br />
<br />
<pre><br />
lm = slicer.app.layoutManager()<br />
sliceLogic = lm.sliceWidget('Red').sliceLogic()<br />
compositeNode = sliceLogic.GetSliceCompositeNode()<br />
compositeNode.SetForegroundOpacity(0.4)<br />
</pre><br />
<br />
== Center the 3D View on the Scene ==<br />
<pre><br />
layoutManager = slicer.app.layoutManager()<br />
threeDWidget = layoutManager.threeDWidget(0)<br />
threeDView = threeDWidget.threeDView()<br />
threeDView.resetFocalPoint()<br />
</pre><br />
<br />
== Display text in a 3D view or slice view ==<br />
<br />
The easiest way to show information overlaid on a viewer is to use corner annotations.<br />
<br />
<pre><br />
view=slicer.app.layoutManager().threeDWidget(0).threeDView()<br />
# Set text to "Something"<br />
view.cornerAnnotation().SetText(vtk.vtkCornerAnnotation.UpperRight,"Something")<br />
# Set color to red<br />
view.cornerAnnotation().GetTextProperty().SetColor(1,0,0)<br />
# Update the view<br />
view.forceRender()<br />
</pre><br />
<br />
== Turning off interpolation ==<br />
<br />
You can turn off interpolation for newly loaded volumes with this script from Steve Pieper.<br />
<br />
<pre><br />
def NoInterpolate(caller,event):<br />
for node in slicer.util.getNodes('*').values():<br />
if node.IsA('vtkMRMLScalarVolumeDisplayNode'):<br />
node.SetInterpolate(0)<br />
<br />
slicer.mrmlScene.AddObserver(slicer.mrmlScene.NodeAddedEvent, NoInterpolate)<br />
</pre><br />
<br />
The below link explains how to put this in your startup script.<br />
<br />
http://www.na-mic.org/Wiki/index.php/AHM2012-Slicer-Python#Refining_the_code_and_UI_with_slicerrc<br />
<br />
<br />
== Customize viewer layout ==<br />
<br />
Show a custom layout of a 3D view on top of the red slice view:<br />
<br />
<pre><br />
customLayout = ("<layout type=\"vertical\" split=\"true\" >"<br />
" <item>"<br />
" <view class=\"vtkMRMLViewNode\" singletontag=\"1\">"<br />
" <property name=\"viewlabel\" action=\"default\">1</property>"<br />
" </view>"<br />
" </item>"<br />
" <item>"<br />
" <view class=\"vtkMRMLSliceNode\" singletontag=\"Red\">"<br />
" <property name=\"orientation\" action=\"default\">Axial</property>"<br />
" <property name=\"viewlabel\" action=\"default\">R</property>"<br />
" <property name=\"viewcolor\" action=\"default\">#F34A33</property>"<br />
" </view>"<br />
" </item>"<br />
"</layout>")<br />
<br />
customLayoutId=501<br />
<br />
layoutManager = slicer.app.layoutManager()<br />
layoutManager.layoutLogic().GetLayoutNode().AddLayoutDescription(customLayoutId, customLayout) <br />
layoutManager.setLayout(customLayoutId)<br />
</pre><br />
<br />
See description of standard layouts (that can be used as examples) here:<br />
https://github.com/Slicer/Slicer/blob/master/Libs/MRML/Logic/vtkMRMLLayoutLogic.cxx<br />
<br />
== Disable certain user interactions in slice views ==<br />
<br />
For example, disable slice browsing using mouse wheel and keyboard shortcuts in the red slice viewer:<br />
<br />
<pre><br />
interactorStyle = slicer.app.layoutManager().sliceWidget('Red').sliceView().sliceViewInteractorStyle()<br />
interactorStyle.SetActionEnabled(interactorStyle.BrowseSlice, False)<br />
</pre><br />
<br />
Hide all slice view controllers:<br />
<pre><br />
lm = slicer.app.layoutManager()<br />
for sliceViewName in lm.sliceViewNames():<br />
lm.sliceWidget(sliceViewName).sliceController().setVisible(False)<br />
</pre><br />
<br />
Hide all 3D view controllers:<br />
<pre><br />
lm = slicer.app.layoutManager()<br />
for viewIndex in range(slicer.app.layoutManager().threeDViewCount):<br />
lm.threeDWidget(0).threeDController().setVisible(False)<br />
</pre><br />
<br />
== Set up custom units in slice view ruler ==<br />
<br />
For microscopy or micro-CT images you may want to switch unit to micrometer instead of the default mm. To do that, 1. change the unit in Application settings / Units and 2. update ruler display settings using the script below (it can be copied to your Application startup script):<br />
<br />
<pre><br />
lm = slicer.app.layoutManager()<br />
for sliceViewName in lm.sliceViewNames():<br />
sliceView = lm.sliceWidget(sliceViewName).sliceView()<br />
displayableManagerCollection = vtk.vtkCollection()<br />
sliceView.getDisplayableManagers(displayableManagerCollection)<br />
for dmIndex in xrange(displayableManagerCollection.GetNumberOfItems()):<br />
displayableManager = displayableManagerCollection.GetItemAsObject(dmIndex)<br />
if not displayableManager.IsA("vtkMRMLRulerDisplayableManager"):<br />
continue<br />
displayableManager.RemoveAllRulerScalePresets()<br />
displayableManager.AddRulerScalePreset( 0.001, 5, 2, "nm", 1000.0)<br />
displayableManager.AddRulerScalePreset( 0.010, 5, 2, "nm", 1000.0)<br />
displayableManager.AddRulerScalePreset( 0.100, 5, 2, "nm", 1000.0)<br />
displayableManager.AddRulerScalePreset( 0.500, 5, 1, "nm", 1000.0)<br />
displayableManager.AddRulerScalePreset( 1.0, 5, 2, "um", 1.0)<br />
displayableManager.AddRulerScalePreset( 5.0, 5, 1, "um", 1.0)<br />
displayableManager.AddRulerScalePreset( 10.0, 5, 2, "um", 1.0)<br />
displayableManager.AddRulerScalePreset( 50.0, 5, 1, "um", 1.0)<br />
displayableManager.AddRulerScalePreset( 100.0, 5, 2, "um", 1.0)<br />
displayableManager.AddRulerScalePreset( 500.0, 5, 1, "um", 1.0)<br />
displayableManager.AddRulerScalePreset(1000.0, 5, 2, "mm", 0.001)<br />
</pre><br />
<br />
== Running an ITK filter in Python using SimpleITK ==<br />
Open the "Sample Data" module and download "MR Head", then paste the following snippet in Python interactor:<br />
<pre><br />
import SimpleITK as sitk<br />
import sitkUtils<br />
inputImage = sitkUtils.PullFromSlicer('MRHead')<br />
filter = sitk.SignedMaurerDistanceMapImageFilter()<br />
outputImage = filter.Execute(inputImage)<br />
sitkUtils.PushToSlicer(outputImage,'outputImage')<br />
</pre><br />
<br />
More information:<br />
* See the SimpleITK documentation for SimpleITK examples: http://www.itk.org/SimpleITKDoxygen/html/examples.html<br />
* sitkUtils in Slicer is used for pushing and pulling images from Slicer to SimpleITK: https://github.com/Slicer/Slicer/blob/master/Base/Python/sitkUtils.py<br />
<br />
== Get current mouse coordinates in a slice view ==<br />
<br />
You can get 3D (RAS) coordinates of the current mouse cursor from the crosshair singleton node as shown in the example below:<br />
<br />
<pre><br />
def onMouseMoved(observer,eventid): <br />
ras=[0,0,0]<br />
crosshairNode.GetCursorPositionRAS(ras)<br />
print(ras)<br />
<br />
crosshairNode=slicer.util.getNode('Crosshair') <br />
crosshairNode.AddObserver(slicer.vtkMRMLCrosshairNode.CursorPositionModifiedEvent, onMouseMoved)<br />
</pre><br />
<br />
== Get DataProbe text ==<br />
<br />
You can get the mouse location in pixel coordinates along with the pixel value at the mouse by hitting the '.' (period) key in a slice view after pasting in the following code.<br />
<br />
<pre><br />
def printDataProbe():<br />
infoWidget = slicer.modules.DataProbeInstance.infoWidget<br />
for layer in ('B', 'F', 'L'):<br />
print(infoWidget.layerNames[layer].text, infoWidget.layerIJKs[layer].text, infoWidget.layerValues[layer].text)<br />
<br />
s = qt.QShortcut(qt.QKeySequence('.'), mainWindow())<br />
s.connect('activated()', printDataProbe)<br />
</pre><br />
<br />
== Thick slab reconstruction and maximum/minimum intensity volume projections ==<br />
<br />
Set up 'red' slice viewer to show thick slab reconstructed from 3 slices:<br />
<pre><br />
sliceNode = slicer.mrmlScene.GetNodeByID('vtkMRMLSliceNodeRed')<br />
appLogic = slicer.app.applicationLogic()<br />
sliceLogic = appLogic.GetSliceLogic(sliceNode)<br />
sliceLayerLogic = sliceLogic.GetBackgroundLayer()<br />
reslice = sliceLayerLogic.GetReslice()<br />
reslice.SetSlabModeToMean()<br />
reslice.SetSlabNumberOfSlices(10) # mean of 10 slices will computed<br />
reslice.SetSlabSliceSpacingFraction(0.3) # spacing between each slice is 0.3 pixel (total 10 * 0.3 = 3 pixel neighborhood)<br />
sliceNode.Modified()<br />
</pre><br />
<br />
Set up 'red' slice viewer to show maximum intensity projection (MIP):<br />
<pre><br />
sliceNode = slicer.mrmlScene.GetNodeByID('vtkMRMLSliceNodeRed')<br />
appLogic = slicer.app.applicationLogic()<br />
sliceLogic = appLogic.GetSliceLogic(sliceNode)<br />
sliceLayerLogic = sliceLogic.GetBackgroundLayer()<br />
reslice = sliceLayerLogic.GetReslice()<br />
reslice.SetSlabModeToMax()<br />
reslice.SetSlabNumberOfSlices(600) # use a large number of slices (600) to cover the entire volume<br />
reslice.SetSlabSliceSpacingFraction(0.5) # spacing between slices are 0.5 pixel (supersampling is useful to reduce interpolation artifacts)<br />
sliceNode.Modified()<br />
</pre><br />
<br />
The projected image is available in a ''vtkImageData'' object by calling ''reslice.GetOutput()''.<br />
<br />
== Change default file type for nodes (that have never been saved yet) ==<br />
Default node can be specified that will be used as a basis of all new storage nodes. This can be used for setting default file extension. For example, change file format to STL for model nodes:<br />
<pre><br />
msn=slicer.vtkMRMLModelStorageNode()<br />
msn.SetDefaultWriteFileExtension('stl')<br />
slicer.mrmlScene.AddDefaultNode(msn)<br />
</pre><br />
<br />
== Change file type for saving for all volumes (with already existing storage nodes) ==<br />
<pre><br />
requiredFileExtension = '.nia'<br />
originalFileExtension = '.nrrd'<br />
volumeNodes = slicer.mrmlScene.GetNodesByClass('vtkMRMLScalarVolumeNode')<br />
volumeNodes.UnRegister(slicer.mrmlScene)<br />
volumeNodes.InitTraversal()<br />
volumeNode = volumeNodes.GetNextItemAsObject()<br />
while volumeNode:<br />
volumeStorageNode = volumeNode.GetStorageNode()<br />
if not volumeStorageNode:<br />
volumeStorageNode = volumeNode.CreateDefaultStorageNode()<br />
slicer.mrmlScene.AddNode(volumeStorageNode)<br />
volumeStorageNode.UnRegister(None)<br />
volumeNode.SetAndObserveStorageNodeID(volumeStorageNode.GetID())<br />
volumeStorageNode.SetFileName(volumeNode.GetName()+requiredFileExtension)<br />
else:<br />
volumeStorageNode.SetFileName(volumeStorageNode.GetFileName().replace(originalFileExtension,requiredFileExtension))<br />
volumeNode = volumeNodes.GetNextItemAsObject()<br />
</pre><br />
<br />
== Segmentations ==<br />
<br />
=== Create a segmentation from a labelmap volume and display in 3D ===<br />
<br />
<pre><br />
labelmapVolumeNode = getNode('label')<br />
seg = slicer.mrmlScene.AddNewNodeByClass('vtkMRMLSegmentationNode')<br />
slicer.modules.segmentations.logic().ImportLabelmapToSegmentationNode(labelmapVolumeNode, seg)<br />
seg.CreateClosedSurfaceRepresentation()<br />
slicer.mrmlScene.RemoveNode(labelmapVolumeNode)<br />
</pre><br />
<br />
The last line is optional. It removes the original labelmap volume so that the same information is not shown twice.<br />
<br />
=== Export labelmap node from segmentation node ===<br />
<br />
<pre><br />
seg = getNode('Segmentation')<br />
labelmapVolumeNode = slicer.mrmlScene.AddNewNodeByClass('vtkMRMLLabelMapVolumeNode')<br />
slicer.modules.segmentations.logic().ExportAllSegmentsToLabelmapNode(seg, labelmapVolumeNode)<br />
</pre><br />
<br />
=== Show a segmentation in 3D ===<br />
Segmentation can only be shown in 3D if closed surface representation (or other 3D-displayable representation) is available. To create closed surface representation:<br />
<pre><br />
segmentation.CreateClosedSurfaceRepresentation()<br />
</pre><br />
<br />
=== Get a representation of a segment ===<br />
Access binary labelmap stored in a segmentation node (without exporting it to a volume node) - if it does not exist, it will return None:<br />
<pre><br />
image = segmentationNode.GetBinaryLabelmapRepresentation(segmentID)<br />
</pre><br />
Get closed surface, if it does not exist, it will return None:<br />
<pre><br />
polydata = segmentationNode.GetClosedSurfaceRepresentation(segmentID)<br />
</pre><br />
Get binary labelmap representation. If it does not exist then it will be created for that single segment. Applies parent transforms by default (if not desired, another argument needs to be added to the end: false):<br />
<pre><br />
import vtkSegmentationCorePython as vtkSegmentationCore<br />
outputOrientedImageData = vtkSegmentationCore.vtkOrientedImageData()<br />
slicer.vtkSlicerSegmentationsModuleLogic.GetSegmentBinaryLabelmapRepresentation(segmentationNode, segmentID, outputOrientedImageData)<br />
</pre><br />
Same as above, for closed surface representation:<br />
<pre><br />
outputPolyData = vtk.vtkPolyData()<br />
slicer.vtkSlicerSegmentationsModuleLogic.GetSegmentClosedSurfaceRepresentation(segmentationNode, segmentID, outputPolyData)<br />
</pre><br />
<br />
=== Convert all segments using default path and conversion parameters ===<br />
<pre><br />
segmentationNode.CreateBinaryLabelmapRepresentation()<br />
</pre><br />
<br />
=== Convert all segments using custom path or conversion parameters ===<br />
Change reference image geometry parameter based on an existing referenceImageData image:<br />
<pre><br />
import vtkSegmentationCorePython as vtkSegmentationCore<br />
referenceGeometry = vtkSegmentationCore.vtkSegmentationConverter.SerializeImageGeometry(referenceImageData)<br />
segmentation.SetConversionParameter(vtkSegmentationCore.vtkSegmentationConverter.GetReferenceImageGeometryParameterName(), referenceGeometry)<br />
</pre><br />
<br />
=== Re-convert using a modified conversion parameter ===<br />
Changing smoothing factor for closed surface generation:<br />
<pre><br />
import vtkSegmentationCorePython as vtkSegmentationCore<br />
segmentation = getNode('Segmentation').GetSegmentation()<br />
<br />
# Turn of surface smoothing<br />
segmentation.SetConversionParameter('Smoothing factor','0.0')<br />
<br />
# Recreate representation using modified parameters (and default conversion path)<br />
segmentation.RemoveRepresentation(vtkSegmentationCore.vtkSegmentationConverter.GetSegmentationClosedSurfaceRepresentationName())<br />
segmentation.CreateRepresentation(vtkSegmentationCore.vtkSegmentationConverter.GetSegmentationClosedSurfaceRepresentationName())<br />
</pre><br />
<br />
=== How to run segment editor effects from a script ===<br />
<br />
Editor effects are complex because they need to handle changing master volumes, undo/redo, masking operations, etc. Therefore, instead of using a segment editor effect, it is simpler to run the underlying filters directly from script.<br />
<br />
This example demonstrates how to run grow from seeds effect in batch mode (without GUI, using qMRMLSegmentEditorWidget):<br />
https://subversion.assembla.com/svn/slicerrt/trunk/SlicerRt/samples/PythonScripts/SegmentGrowCut/SegmentGrowCutSimple.py<br />
<br />
This example demonstrates how to perform grow from seeds using VTK filters:<br />
https://subversion.assembla.com/svn/slicerrt/trunk/SlicerRt/samples/PythonScripts/SegmentGrowCut/SegmentGrowCut.py<br />
<br />
== Accessing views, renderers, and cameras ==<br />
<br />
Iterate through all 3D views in current layout:<br />
<br />
<pre><br />
layoutManager = slicer.app.layoutManager()<br />
for threeDViewIndex in range(layoutManager.threeDViewCount) :<br />
view = layoutManager.threeDWidget(threeDViewIndex).threeDView()<br />
threeDViewNode = view.mrmlViewNode()<br />
cameraNode = slicer.modules.cameras.logic().GetViewActiveCameraNode(threeDViewNode)<br />
print('View node for 3D widget ' + str(threeDViewIndex))<br />
print(' Name: ' + threeDViewNode .GetName())<br />
print(' ID: ' + threeDViewNode .GetID())<br />
print(' Camera ID: ' + cameraNode.GetID())<br />
</pre><br />
<br />
Iterate through all slice views in current layout:<br />
<br />
<pre><br />
layoutManager = slicer.app.layoutManager()<br />
for sliceViewName in layoutManager.sliceViewNames():<br />
view = layoutManager.sliceWidget(sliceViewName).sliceView()<br />
sliceNode = view.mrmlSliceNode()<br />
sliceLogic = slicer.app.applicationLogic().GetSliceLogic(sliceNode)<br />
compositeNode = sliceLogic.GetSliceCompositeNode()<br />
print('Slice view ' + str(sliceViewName))<br />
print(' Name: ' + sliceNode.GetName())<br />
print(' ID: ' + sliceNode.GetID())<br />
print(' Background volume: {0}'.format(compositeNode.GetBackgroundVolumeID()))<br />
print(' Foreground volume: {0} (opacity: {1})'.format(compositeNode.GetForegroundVolumeID(), compositeNode.GetForegroundOpacity()))<br />
print(' Label volume: {0} (opacity: {1})'.format(compositeNode.GetLabelVolumeID(), compositeNode.GetLabelOpacity()))<br />
</pre><br />
<br />
For low-level manipulation of views, it is possible to access VTK render windows, renderers and cameras of views in the current layout.<br />
<pre><br />
renderWindow = view.renderWindow()<br />
renderers = renderWindow.GetRenderers()<br />
renderer = renderers.GetItemAsObject(0)<br />
camera = cameraNode.GetCamera()<br />
</pre><br />
<br />
== Subject hierarchy == <br />
==== Get the pseudo-singleton subject hierarchy node ====<br />
It manages the whole hierarchy and provides functions to access and manipulate<br />
shNode = slicer.vtkMRMLSubjectHierarchyNode.GetSubjectHierarchyNode(slicer.mrmlScene)<br />
<br />
==== Create subject hierarchy item ====<br />
# If it is for a data node, it is automatically created, but the create function can be used to set parent:<br />
shNode.CreateItem(parentItemID, dataNode)<br />
# If it is a hierarchy item without a data node, then the create function must be used:<br />
shNode.CreateSubjectItem(parentItemID, name)<br />
shNode.CreateFolderItem(parentItemID, name)<br />
shNode.CreateHierarchyItem(parentItemID, name, level) # Advanced method to set level attribute manually (usually subject, study, or folder, but it can be a virtual branch for example)<br />
<br />
==== Get subject hierarchy item ====<br />
Items in subject hierarchy are uniquely identified by integer IDs<br />
# Get scene item ID first because it is the root item:<br />
sceneItemID = shNode.GetSceneItemID()<br />
# Get direct child by name<br />
subjectItemID = shNode.GetItemChildWithName(sceneItemID, 'Subject_1')<br />
# Get item for data node<br />
itemID = shNode.GetItemByDataNode(dataNode)<br />
# Get item by UID (such as DICOM)<br />
itemID = shNode.GetItemByUID(slicer.vtkMRMLSubjectHierarchyConstants.GetDICOMUIDName(), seriesInstanceUid)<br />
itemID = shNode.GetItemByUIDList(slicer.vtkMRMLSubjectHierarchyConstants.GetDICOMInstanceUIDName(), instanceUID)<br />
# Invalid item ID for checking validity of a given ID (most functions return the invalid ID when item is not found)<br />
invalidItemID = slicer.vtkMRMLSubjectHierarchyNode.GetInvalidItemID()<br />
<br />
==== Traverse children of a subject hierarchy item ====<br />
children = vtk.vtkIdList()<br />
shNode.GetItemChildren(parent, children)<br />
for i in xrange(children.GetNumberOfIds()):<br />
child = children.GetId(i)<br />
...<br />
<br />
==== Manipulate subject hierarchy item ====<br />
Instead of node operations on the individual subject hierarchy nodes, item operations are performed on the one subject hierarchy node.<br />
# Set item name<br />
shNode.SetItemName(itemID, 'NewName')<br />
# Set item parent (reparent)<br />
shNode.SetItemParent(itemID, newParentItemID)<br />
# Set visibility of data nodes associated to items in a branch (or a leaf item)<br />
shNode.SetDisplayVisibilityForBranch(itemID, 1)<br />
<br />
==== Filter items in TreeView or ComboBox ====<br />
Displayed items can be filtered using ''setAttributeFilter'' method. An example of the usage can be found in the [https://github.com/Slicer/Slicer/blob/e66e3b08e35384526528e6ae678e9ec9f079f286/Applications/SlicerApp/Testing/Python/SubjectHierarchyGenericSelfTest.py#L352-L360 unit test]. Modified version here:<br />
print shTreeView.displayedItemCount() # 5<br />
shTreeView.setAttributeFilter('DICOM.Modality') # Nodes must have this attribute<br />
print shTreeView.displayedItemCount() # 3<br />
shTreeView.setAttributeFilter('DICOM.Modality','CT') # Have attribute and equal 'CT'<br />
print shTreeView.displayedItemCount() # 1<br />
shTreeView.removeAttributeFilter()<br />
print shTreeView.displayedItemCount() # 5</div>Dzenanzhttps://www.slicer.org/w/index.php?title=Documentation/Nightly/ScriptRepository&diff=53618Documentation/Nightly/ScriptRepository2017-07-26T18:22:11Z<p>Dzenanz: Adding subject hierarchy filtering example</p>
<hr />
<div><noinclude>{{documentation/versioncheck}}</noinclude><br />
__TOC__<br />
<br />
<br />
=Community-contributed modules=<br />
<br />
Usage: save the .py file to a directory, add the directory to the additional module paths in the Slicer application settings (choose in the menu: Edit / Application settings, click Modules, click >> next to Additional module paths, click Add, and choose the .py file's location).<br />
<br />
==Filters==<br />
* [https://raw.github.com/pieper/VolumeMasker/master/VolumeMasker.py VolumeMasker.py]: Update a target volume with the results of setting all input volume voxels to 0 except for those that correspond to a selected label value in an input label map (Used for example in the volume rendering in [https://www.youtube.com/watch?v=dfu2gugHLHs this video).<br />
<br />
==DICOM==<br />
* [https://gist.github.com/pieper/6186477 dicom header browser] to easily scroll through dicom files using dcmdump.<br />
* [https://subversion.assembla.com/svn/slicerrt/trunk/SlicerRt/src/BatchProcessing SlicerRT batch processing] to batch convert RT structure sets to labelmap NRRD files.<br />
<br />
==Informatics==<br />
* [https://subversion.assembla.com/svn/slicerrt/trunk/SlicerRt/sandbox/MarkupsInfoModule/MarkupsInfo.py MarkupsInfo.py]: Compute the total length between all the points of a markup list.<br />
* [https://subversion.assembla.com/svn/slicerrt/trunk/SlicerRt/sandbox/LineProfile/LineProfile.py LineProfile.py]: Compute intensity profile in a volume along a line.<br />
<br />
=Community-contributed examples=<br />
<br />
Usage: Copy-paste the shown code lines or linked .py file contents into Python console in Slicer.<br />
<br />
==Capture==<br />
* Capture the full Slicer screen and save it into a file<br />
img = qt.QPixmap.grabWidget(slicer.util.mainWindow()).toImage()<br />
img.save('c:/tmp/test.png')<br />
* Capture all the views save it into a file:<br />
<pre><br />
import ScreenCapture<br />
cap = ScreenCapture.ScreenCaptureLogic()<br />
cap.showViewControllers(False)<br />
cap.captureImageFromView(None,'c:/tmp/test.png')<br />
cap.showViewControllers(True)<br />
</pre><br />
* Capture a single view:<br />
<pre><br />
viewNodeID = 'vtkMRMLViewNode1'<br />
import ScreenCapture<br />
cap = ScreenCapture.ScreenCaptureLogic()<br />
view = logic.viewFromNode(slicer.mrmlScene.GetNodeByID(viewNodeID))<br />
cap.captureImageFromView(view,'c:/tmp/test.png')<br />
</pre><br />
Common values for viewNodeID: vtkMRMLSliceNodeRed, vtkMRMLSliceNodeYellow, vtkMRMLSliceNodeGreen, vtkMRMLViewNode1, vtkMRMLViewNode2. <br />
The ScreenCapture module can also create video animations of rotating views, slice sweeps, etc.<br />
<br />
==Launching Slicer==<br />
* How to open an .mrb file with Slicer at the command line?<br />
Slicer.exe --python-code "slicer.util.loadScene( 'f:/2013-08-23-Scene.mrb' )"<br />
* How to run a script in the Slicer environment in batch mode (without showing any graphical user interface)?<br />
Slicer.exe --python-code "doSomething; doSomethingElse; etc." --testing --no-splash --no-main-window<br />
<br />
==Load volume from file==<br />
When loading a volume from file, it is recommended to set returnNode=True to retrieve the loaded volume node.<br />
<pre><br />
[success, loadedVolumeNode] = slicer.util.loadVolume('c:/Users/abc/Documents/MRHead.nrrd', returnNode=True)<br />
</pre><br />
<br />
* Get a MRML node in the scene based on the node name and call methods of that object. For the MRHead sample data:<br />
vol=slicer.util.getNode('MR*')<br />
vol.GetImageData().GetDimensions()<br />
<br />
==DICOM==<br />
=== How to access tags of DICOM images imported into Slicer? For example, to print the first patient's first study's first series' "0020,0032" field:===<br />
db=slicer.dicomDatabase<br />
patientList=db.patients()<br />
studyList=db.studiesForPatient(patientList[0])<br />
seriesList=db.seriesForStudy(studyList[0])<br />
fileList=db.filesForSeries(seriesList[0])<br />
print db.fileValue(fileList[0],'0020,0032')<br />
<br />
=== How to access tag of a volume loaded from DICOM? For example, get the patient position stored in a volume:===<br />
volumeName='2: ENT IMRT'<br />
n=slicer.util.getNode(volumeName)<br />
instUids=n.GetAttribute('DICOM.instanceUIDs').split()<br />
filename=slicer.dicomDatabase.fileForInstance(instUids[0])<br />
print slicer.dicomDatabase.fileValue(filename,'0018,5100')<br />
<br />
=== How to access tag of an item in the Subject Hierachy tree? For example, get the content time tag of a structure set:===<br />
rtStructName = '3: RTSTRUCT: PROS'<br />
rtStructNode = slicer.util.getNode(rtStructName)<br />
shNode = slicer.vtkMRMLSubjectHierarchyNode.GetSubjectHierarchyNode(slicer.mrmlScene)<br />
rtStructShItemID = shNode.GetItemByDataNode(rtStructNode)<br />
ctSliceInstanceUids = shNode.GetItemAttribute(rtStructShItemID, 'DICOM.ReferencedInstanceUIDs').split()<br />
filename = slicer.dicomDatabase.fileForInstance(ctSliceInstanceUids[0])<br />
print slicer.dicomDatabase.fileValue(filename,'0008,0033')<br />
<br />
=== How can I convert DICOM to NRRD on the command line?===<br />
<br />
/Applications/Slicer-4.6.2.app/Contents/MacOS/Slicer --no-main-window --python-code "node=slicer.util.loadVolume('/tmp/series/im0.dcm', returnNode=True)[1]; slicer.util.saveNode(node, '/tmp/output.nrrd'); exit()"<br />
<br />
The same can be done on windows by using the top level Slicer.exe. Be sure to use forward slashes in the pathnames within quotes on the command line.<br />
<br />
==Toolbar functions==<br />
* How to turn on slice intersections in the crosshair menu on the toolbar:<br />
viewNodes = slicer.mrmlScene.GetNodesByClass('vtkMRMLSliceCompositeNode')<br />
viewNodes.UnRegister(slicer.mrmlScene)<br />
viewNodes.InitTraversal()<br />
viewNode = viewNodes.GetNextItemAsObject()<br />
while viewNode:<br />
viewNode.SetSliceIntersectionVisibility(1)<br />
viewNode = viewNodes.GetNextItemAsObject()<br />
<br />
How to find similar functions? For this one I searched for "slice intersections" text in the whole slicer source code, found that the function is implemented in Base\QTGUI\qSlicerViewersToolBar.cxx, then translated the qSlicerViewersToolBarPrivate::setSliceIntersectionVisible(bool visible) method to Python.<br />
<br />
==Manipulating objects in the slice viewer==<br />
* How to define/edit a circular region of interest in a slice viewer?<br />
<br />
Drop two markup points on a slice view and copy-paste the code below into the Python console. After this, as you move the markups you’ll see a circle following the markups.<br />
<br />
<pre><br />
# Update the sphere from the fiducial points<br />
def UpdateSphere(param1, param2): <br />
import math<br />
centerPointCoord = [0.0, 0.0, 0.0]<br />
markups.GetNthFiducialPosition(0,centerPointCoord)<br />
circumferencePointCoord = [0.0, 0.0, 0.0]<br />
markups.GetNthFiducialPosition(1,circumferencePointCoord)<br />
sphere.SetCenter(centerPointCoord)<br />
radius=math.sqrt((centerPointCoord[0]-circumferencePointCoord[0])**2+(centerPointCoord[1]-circumferencePointCoord[1])**2+(centerPointCoord[2]-circumferencePointCoord[2])**2)<br />
sphere.SetRadius(radius)<br />
sphere.SetPhiResolution(30)<br />
sphere.SetThetaResolution(30)<br />
sphere.Update()<br />
<br />
# Get markup node from scene<br />
markups=slicer.util.getNode('F')<br />
sphere = vtk.vtkSphereSource()<br />
UpdateSphere(0,0)<br />
<br />
# Create model node and add to scene<br />
modelsLogic = slicer.modules.models.logic()<br />
model = modelsLogic.AddModel(sphere.GetOutput())<br />
model.GetDisplayNode().SetSliceIntersectionVisibility(True)<br />
model.GetDisplayNode().SetSliceIntersectionThickness(3)<br />
model.GetDisplayNode().SetColor(1,1,0)<br />
<br />
# Call UpdateSphere whenever the fiducials are changed<br />
markups.AddObserver("ModifiedEvent", UpdateSphere, 2)<br />
</pre><br />
<br />
== Switching to markup fiducial placement mode ==<br />
<br />
To activate a fiducial placement mode, both interaction mode has to be set and a fiducial node has to be selected:<br />
<br />
<pre><br />
interactionNode = slicer.app.applicationLogic().GetInteractionNode()<br />
selectionNode = slicer.app.applicationLogic().GetSelectionNode()<br />
selectionNode.SetReferenceActivePlaceNodeClassName("vtkMRMLMarkupsFiducialNode")<br />
fiducialNode = slicer.vtkMRMLMarkupsFiducialNode()<br />
slicer.mrmlScene.AddNode(fiducialNode)<br />
fiducialNode.CreateDefaultDisplayNodes() <br />
selectionNode.SetActivePlaceNodeID(fiducialNode.GetID())<br />
interactionNode.SetCurrentInteractionMode(interactionNode.Place)<br />
</pre><br />
<br />
== Show a context menu when a markup point is clicked in a slice or 3D view ==<br />
<br />
<pre><br />
<br />
# Example actions to perform<br />
<br />
def action1():<br />
print('Action1 on markup '+str(slicer.clickedMarkupIndex))<br />
<br />
def action2():<br />
print('Action2 on markup '+str(slicer.clickedMarkupIndex))<br />
<br />
def action3():<br />
print('Action3 on markup '+str(slicer.clickedMarkupIndex))<br />
<br />
# Clicked markup index is saved here to let the action<br />
# know which markup needs to be manipulated.<br />
slicer.clickedMarkupIndex = -1<br />
<br />
# Create a simple menu<br />
<br />
menu = qt.QMenu()<br />
a1 = qt.QAction("Test", slicer.util.mainWindow())<br />
a1.connect('triggered()', action1)<br />
menu.addAction(a1)<br />
a2 = qt.QAction("Action", slicer.util.mainWindow())<br />
a2.connect('triggered()', action1)<br />
menu.addAction(a2)<br />
a3 = qt.QAction("Here", slicer.util.mainWindow())<br />
a3.connect('triggered()', action1)<br />
menu.addAction(a3)<br />
<br />
# Add observer to a markup fiducial list<br />
<br />
@vtk.calldata_type(vtk.VTK_INT)<br />
def markupClickedCallback(caller, eventId, callData):<br />
slicer.clickedMarkupIndex = callData<br />
print('Open menu on markup '+str(slicer.clickedMarkupIndex))<br />
menu.move(qt.QCursor.pos())<br />
menu.show()<br />
<br />
markupsNode = getNode('F')<br />
observerTag = markupsNode.AddObserver(slicer.vtkMRMLMarkupsNode.PointClickedEvent, markupClickedCallback)<br />
<br />
</pre><br />
<br />
== Add a texture mapped plane to the scene as a model ==<br />
Note that model textures are not exposed in the GUI and are not saved in the scene<br />
<pre><br />
# use dummy image data here<br />
e = vtk.vtkImageEllipsoidSource()<br />
<br />
scene = slicer.mrmlScene<br />
<br />
# Create model node<br />
model = slicer.vtkMRMLModelNode()<br />
model.SetScene(scene)<br />
model.SetName(scene.GenerateUniqueName("2DImageModel"))<br />
<br />
planeSource = vtk.vtkPlaneSource()<br />
model.SetAndObservePolyData(planeSource.GetOutput())<br />
<br />
# Create display node<br />
modelDisplay = slicer.vtkMRMLModelDisplayNode()<br />
modelDisplay.SetColor(1,1,0) # yellow<br />
modelDisplay.SetBackfaceCulling(0)<br />
modelDisplay.SetScene(scene)<br />
scene.AddNode(modelDisplay)<br />
model.SetAndObserveDisplayNodeID(modelDisplay.GetID())<br />
<br />
# Add to scene<br />
modelDisplay.SetAndObserveTextureImageData(e.GetOutput())<br />
scene.AddNode(model) <br />
<br />
<br />
transform = slicer.vtkMRMLLinearTransformNode()<br />
scene.AddNode(transform) <br />
model.SetAndObserveTransformNodeID(transform.GetID())<br />
<br />
vTransform = vtk.vtkTransform()<br />
vTransform.Scale(50,50,50)<br />
vTransform.RotateX(30)<br />
transform.SetAndObserveMatrixTransformToParent(vTransform.GetMatrix())<br />
</pre><br />
<br />
<br />
== Export entire scene as VRML ==<br />
<br />
Save all surface meshes displayed in the scene (models, markups, etc). Solid colors and coloring by scalar is preserved. Textures are not supported.<br />
<br />
<pre><br />
exporter = vtk.vtkVRMLExporter()<br />
exporter.SetRenderWindow(slicer.app.layoutManager().threeDWidget(0).threeDView().renderWindow())<br />
exporter.SetFileName('C:/tmp/something.wrl')<br />
exporter.Write()<br />
</pre><br />
<br />
== Export model to Blender, including color by scalar ==<br />
<br />
<pre><br />
modelNode = getNode("Model")<br />
plyFilePath = "c:/tmp/model.ply"<br />
<br />
modelDisplayNode = modelNode.GetDisplayNode()<br />
triangles = vtk.vtkTriangleFilter()<br />
triangles.SetInputConnection(modelDisplayNode.GetOutputPolyDataConnection())<br />
<br />
plyWriter = vtk.vtkPLYWriter()<br />
plyWriter.SetInputConnection(triangles.GetOutputPort())<br />
lut = vtk.vtkLookupTable()<br />
lut.DeepCopy(modelDisplayNode.GetColorNode().GetLookupTable())<br />
lut.SetRange(modelDisplayNode.GetScalarRange())<br />
plyWriter.SetLookupTable(lut)<br />
plyWriter.SetArrayName(modelDisplayNode.GetActiveScalarName())<br />
<br />
plyWriter.SetFileName(plyFilePath)<br />
plyWriter.Write()<br />
</pre><br />
<br />
== Export a fiber tracts to Blender, including color ==<br />
<br />
<pre><br />
lineDisplayNode = getNode("*LineDisplay*")<br />
plyFilePath = "/tmp/fibers.ply"<br />
<br />
tuber = vtk.vtkTubeFilter()<br />
tuber.SetInputData(lineDisplayNode.GetOutputPolyData())<br />
tuber.Update()<br />
tubes = tuber.GetOutput()<br />
scalars = tubes.GetPointData().GetArray(0)<br />
scalars.SetName("scalars")<br />
<br />
triangles = vtk.vtkTriangleFilter()<br />
triangles.SetInputData(tubes)<br />
<br />
colorNode = lineDisplayNode.GetColorNode()<br />
lookupTable = vtk.vtkLookupTable()<br />
lookupTable.DeepCopy(colorNode.GetLookupTable())<br />
lookupTable.SetTableRange(0,1)<br />
<br />
plyWriter = vtk.vtkPLYWriter()<br />
plyWriter.SetInputData(triangles.GetOutput())<br />
plyWriter.SetLookupTable(lookupTable)<br />
plyWriter.SetArrayName("scalars")<br />
<br />
plyWriter.SetFileName(plyFilePath)<br />
plyWriter.Write()<br />
</pre><br />
<br />
== Clone a volume ==<br />
This example shows how to clone the MRHead sample volume, including its pixel data and display settings.<br />
<pre><br />
sourceVolumeNode = slicer.util.getNode('MRHead')<br />
volumesLogic = slicer.modules.volumes.logic()<br />
clonedVolumeNode = volumesLogic.CloneVolume(slicer.mrmlScene, sourceVolumeNode, 'Cloned volume')<br />
</pre><br />
<br />
== Create a new volume ==<br />
This example shows how to create a new empty volume.<br />
<pre><br />
imageSize=[512, 512, 512]<br />
imageSpacing=[1.0, 1.0, 1.0]<br />
voxelType=vtk.VTK_UNSIGNED_CHAR<br />
# Create an empty image volume<br />
imageData=vtk.vtkImageData()<br />
imageData.SetDimensions(imageSize)<br />
imageData.AllocateScalars(voxelType, 1)<br />
thresholder=vtk.vtkImageThreshold()<br />
thresholder.SetInputData(imageData)<br />
thresholder.SetInValue(0)<br />
thresholder.SetOutValue(0)<br />
# Create volume node<br />
volumeNode=slicer.vtkMRMLScalarVolumeNode()<br />
volumeNode.SetSpacing(imageSpacing)<br />
volumeNode.SetImageDataConnection(thresholder.GetOutputPort())<br />
# Add volume to scene<br />
slicer.mrmlScene.AddNode(volumeNode)<br />
displayNode=slicer.vtkMRMLScalarVolumeDisplayNode()<br />
slicer.mrmlScene.AddNode(displayNode)<br />
colorNode = slicer.util.getNode('Grey')<br />
displayNode.SetAndObserveColorNodeID(colorNode.GetID())<br />
volumeNode.SetAndObserveDisplayNodeID(displayNode.GetID())<br />
volumeNode.CreateDefaultStorageNode()<br />
</pre><br />
<br />
== Modify voxels in a volume ==<br />
<br />
Typically the fastest and simplest way of modifying voxels is by using numpy operators. Voxels can be retrieved in a numpy array using the `array` method and modified using standard numpy methods. For example, threshold a volume:<br />
<br />
<pre><br />
nodeName = 'MRHead'<br />
thresholdValue = 100<br />
voxelArray = array(nodeName) # get voxels as numpy array<br />
voxelArray[voxelArray < thresholdValue] = 0 # modify voxel values<br />
getNode(nodeName).Modified() # at the end of all processing, notify Slicer that the image modification is completed<br />
</pre><br />
<br />
This example shows how to change voxels values of the MRHead sample volume.<br />
The values will be computed by function f(r,a,s,) = (r-10)*(r-10)+(a+15)*(a+15)+s*s.<br />
<pre><br />
volumeNode=slicer.util.getNode('MRHead')<br />
ijkToRas = vtk.vtkMatrix4x4()<br />
volumeNode.GetIJKToRASMatrix(ijkToRas)<br />
imageData=volumeNode.GetImageData()<br />
extent = imageData.GetExtent()<br />
for k in xrange(extent[4], extent[5]+1):<br />
for j in xrange(extent[2], extent[3]+1):<br />
for i in xrange(extent[0], extent[1]+1):<br />
position_Ijk=[i, j, k, 1]<br />
position_Ras=ijkToRas.MultiplyPoint(position_Ijk)<br />
r=position_Ras[0]<br />
a=position_Ras[1]<br />
s=position_Ras[2] <br />
functionValue=(r-10)*(r-10)+(a+15)*(a+15)+s*s<br />
imageData.SetScalarComponentFromDouble(i,j,k,0,functionValue)<br />
imageData.SetScalarComponentFromFloat(distortionVectorPosition_Ijk[0], distortionVectorPosition_Ijk[1], distortionVectorPosition_Ijk[2], 0, fillValue)<br />
imageData.Modified()<br />
</pre><br />
<br />
== Access values in a DTI tensor volume ==<br />
This example shows how to access individual tensors at the voxel level.<br />
<br />
First load your DWI volume and estimate tensors to produce a DTI volume called ‘Output DTI Volume’<br />
<br />
Then open the python window: View->Python interactor<br />
<br />
Use this command to access tensors through numpy:<br />
<br />
<pre><br />
tensors = array('Output DTI Volume')<br />
</pre><br />
<br />
Type the following code into the Python window to access all tensor components using vtk commands:<br />
<br />
<pre><br />
volumeNode=slicer.util.getNode('Output DTI Volume')<br />
imageData=volumeNode.GetImageData()<br />
tensors = imageData.GetPointData().GetTensors()<br />
extent = imageData.GetExtent()<br />
idx = 0<br />
for k in xrange(extent[4], extent[5]+1):<br />
for j in xrange(extent[2], extent[3]+1):<br />
for i in xrange(extent[0], extent[1]+1):<br />
tensors.GetTuple9(idx)<br />
idx += 1<br />
</pre><br />
<br />
== Change window/level (brightness/contrast) or colormap of a volume ==<br />
This example shows how to change window/level of the MRHead sample volume.<br />
<pre><br />
volumeNode = getNode('MRHead')<br />
displayNode = volumeNode.GetDisplayNode()<br />
displayNode.AutoWindowLevelOff()<br />
displayNode.SetWindow(50)<br />
displayNode.SetLevel(100)<br />
</pre><br />
<br />
Change color mapping from grayscale to rainbow:<br />
<pre><br />
displayNode.SetAndObserveColorNodeID('vtkMRMLColorTableNodeRainbow')<br />
</pre><br />
<br />
== Manipulate a Slice View ==<br />
<br />
<pre><br />
lm = slicer.app.layoutManager()<br />
red = lm.sliceWidget('Red')<br />
redLogic = red.sliceLogic()<br />
# Print current slice offset position<br />
print redLogic.GetSliceOffset()<br />
# Change slice position<br />
redLogic.SetSliceOffset(20)<br />
</pre><br />
<br />
== Save a series of images from a Slice View ==<br />
<br />
You can use ScreenCapture module to capture series of images. To do it programmatically, save the following into a file such as '/tmp/record.py' and then in the slicer python console type "execfile('/tmp/record.py')"<br />
<br />
<pre><br />
layoutName = 'Green'<br />
imagePathPattern = '/tmp/image-%03d.png'<br />
steps = 10<br />
<br />
widget = slicer.app.layoutManager().sliceWidget(layoutName)<br />
view = widget.sliceView()<br />
logic = widget.sliceLogic()<br />
bounds = [0,]*6<br />
logic.GetSliceBounds(bounds)<br />
<br />
for step in range(steps):<br />
offset = bounds[4] + step/(1.*steps) * (bounds[5]-bounds[4])<br />
logic.SetSliceOffset(offset)<br />
view.forceRender()<br />
image = qt.QPixmap.grabWidget(view).toImage()<br />
image.save(imagePathPattern % step)<br />
</pre><br />
<br />
== Save the scene into a new directory ==<br />
<br />
<pre><br />
# Create a new directory where the scene will be saved into<br />
import time<br />
sceneSaveDirectory = slicer.app.temporaryPath + "/saved-scene-" + time.strftime("%Y%m%d-%H%M%S")<br />
if not os.access(sceneSaveDirectory, os.F_OK):<br />
os.makedirs(sceneSaveDirectory)<br />
<br />
# Save the scene<br />
if slicer.app.applicationLogic().SaveSceneToSlicerDataBundleDirectory(sceneSaveDirectory, None):<br />
logging.info("Scene saved to: {0}".format(sceneSaveDirectory))<br />
else:<br />
logging.error("Scene saving failed") <br />
</pre><br />
<br />
== Save the scene into a single MRB file ==<br />
<pre><br />
# Generate file name<br />
import time<br />
sceneSaveFilename = slicer.app.temporaryPath + "/saved-scene-" + time.strftime("%Y%m%d-%H%M%S") + ".mrb"<br />
<br />
# Save scene<br />
if slicer.util.saveScene(sceneSaveFilename):<br />
logging.info("Scene saved to: {0}".format(sceneSaveFilename))<br />
else:<br />
logging.error("Scene saving failed") <br />
</pre><br />
<br />
== Show a volume in the Slice Views ==<br />
<br />
<pre><br />
volumeNode = slicer.util.getNode('YourVolumeNode')<br />
applicationLogic = slicer.app.applicationLogic()<br />
selectionNode = applicationLogic.GetSelectionNode()<br />
selectionNode.SetSecondaryVolumeID(volumeNode.GetID())<br />
applicationLogic.PropagateForegroundVolumeSelection(0) <br />
</pre><br />
<br />
or<br />
<br />
<pre><br />
n = slicer.util.getNode('YourVolumeNode')<br />
for color in ['Red', 'Yellow', 'Green']:<br />
slicer.app.layoutManager().sliceWidget(color).sliceLogic().GetSliceCompositeNode().SetForegroundVolumeID(n.GetID())<br />
</pre><br />
<br />
== Change opacity of foreground volume in the Slice Views ==<br />
<br />
<pre><br />
lm = slicer.app.layoutManager()<br />
sliceLogic = lm.sliceWidget('Red').sliceLogic()<br />
compositeNode = sliceLogic.GetSliceCompositeNode()<br />
compositeNode.SetForegroundOpacity(0.4)<br />
</pre><br />
<br />
== Center the 3D View on the Scene ==<br />
<pre><br />
layoutManager = slicer.app.layoutManager()<br />
threeDWidget = layoutManager.threeDWidget(0)<br />
threeDView = threeDWidget.threeDView()<br />
threeDView.resetFocalPoint()<br />
</pre><br />
<br />
== Display text in a 3D view or slice view ==<br />
<br />
The easiest way to show information overlaid on a viewer is to use corner annotations.<br />
<br />
<pre><br />
view=slicer.app.layoutManager().threeDWidget(0).threeDView()<br />
# Set text to "Something"<br />
view.cornerAnnotation().SetText(vtk.vtkCornerAnnotation.UpperRight,"Something")<br />
# Set color to red<br />
view.cornerAnnotation().GetTextProperty().SetColor(1,0,0)<br />
# Update the view<br />
view.forceRender()<br />
</pre><br />
<br />
== Turning off interpolation ==<br />
<br />
You can turn off interpolation for newly loaded volumes with this script from Steve Pieper.<br />
<br />
<pre><br />
def NoInterpolate(caller,event):<br />
for node in slicer.util.getNodes('*').values():<br />
if node.IsA('vtkMRMLScalarVolumeDisplayNode'):<br />
node.SetInterpolate(0)<br />
<br />
slicer.mrmlScene.AddObserver(slicer.mrmlScene.NodeAddedEvent, NoInterpolate)<br />
</pre><br />
<br />
The below link explains how to put this in your startup script.<br />
<br />
http://www.na-mic.org/Wiki/index.php/AHM2012-Slicer-Python#Refining_the_code_and_UI_with_slicerrc<br />
<br />
<br />
== Customize viewer layout ==<br />
<br />
Show a custom layout of a 3D view on top of the red slice view:<br />
<br />
<pre><br />
customLayout = ("<layout type=\"vertical\" split=\"true\" >"<br />
" <item>"<br />
" <view class=\"vtkMRMLViewNode\" singletontag=\"1\">"<br />
" <property name=\"viewlabel\" action=\"default\">1</property>"<br />
" </view>"<br />
" </item>"<br />
" <item>"<br />
" <view class=\"vtkMRMLSliceNode\" singletontag=\"Red\">"<br />
" <property name=\"orientation\" action=\"default\">Axial</property>"<br />
" <property name=\"viewlabel\" action=\"default\">R</property>"<br />
" <property name=\"viewcolor\" action=\"default\">#F34A33</property>"<br />
" </view>"<br />
" </item>"<br />
"</layout>")<br />
<br />
customLayoutId=501<br />
<br />
layoutManager = slicer.app.layoutManager()<br />
layoutManager.layoutLogic().GetLayoutNode().AddLayoutDescription(customLayoutId, customLayout) <br />
layoutManager.setLayout(customLayoutId)<br />
</pre><br />
<br />
See description of standard layouts (that can be used as examples) here:<br />
https://github.com/Slicer/Slicer/blob/master/Libs/MRML/Logic/vtkMRMLLayoutLogic.cxx<br />
<br />
== Disable certain user interactions in slice views ==<br />
<br />
For example, disable slice browsing using mouse wheel and keyboard shortcuts in the red slice viewer:<br />
<br />
<pre><br />
interactorStyle = slicer.app.layoutManager().sliceWidget('Red').sliceView().sliceViewInteractorStyle()<br />
interactorStyle.SetActionEnabled(interactorStyle.BrowseSlice, False)<br />
</pre><br />
<br />
Hide all slice view controllers:<br />
<pre><br />
lm = slicer.app.layoutManager()<br />
for sliceViewName in lm.sliceViewNames():<br />
lm.sliceWidget(sliceViewName).sliceController().setVisible(False)<br />
</pre><br />
<br />
Hide all 3D view controllers:<br />
<pre><br />
lm = slicer.app.layoutManager()<br />
for viewIndex in range(slicer.app.layoutManager().threeDViewCount):<br />
lm.threeDWidget(0).threeDController().setVisible(False)<br />
</pre><br />
<br />
== Running an ITK filter in Python using SimpleITK ==<br />
Open the "Sample Data" module and download "MR Head", then paste the following snippet in Python interactor:<br />
<pre><br />
import SimpleITK as sitk<br />
import sitkUtils<br />
inputImage = sitkUtils.PullFromSlicer('MRHead')<br />
filter = sitk.SignedMaurerDistanceMapImageFilter()<br />
outputImage = filter.Execute(inputImage)<br />
sitkUtils.PushToSlicer(outputImage,'outputImage')<br />
</pre><br />
<br />
More information:<br />
* See the SimpleITK documentation for SimpleITK examples: http://www.itk.org/SimpleITKDoxygen/html/examples.html<br />
* sitkUtils in Slicer is used for pushing and pulling images from Slicer to SimpleITK: https://github.com/Slicer/Slicer/blob/master/Base/Python/sitkUtils.py<br />
<br />
== Get current mouse coordinates in a slice view ==<br />
<br />
You can get 3D (RAS) coordinates of the current mouse cursor from the crosshair singleton node as shown in the example below:<br />
<br />
<pre><br />
def onMouseMoved(observer,eventid): <br />
ras=[0,0,0]<br />
crosshairNode.GetCursorPositionRAS(ras)<br />
print(ras)<br />
<br />
crosshairNode=slicer.util.getNode('Crosshair') <br />
crosshairNode.AddObserver(slicer.vtkMRMLCrosshairNode.CursorPositionModifiedEvent, onMouseMoved)<br />
</pre><br />
<br />
== Get DataProbe text ==<br />
<br />
You can get the mouse location in pixel coordinates along with the pixel value at the mouse by hitting the '.' (period) key in a slice view after pasting in the following code.<br />
<br />
<pre><br />
def printDataProbe():<br />
infoWidget = slicer.modules.DataProbeInstance.infoWidget<br />
for layer in ('B', 'F', 'L'):<br />
print(infoWidget.layerNames[layer].text, infoWidget.layerIJKs[layer].text, infoWidget.layerValues[layer].text)<br />
<br />
s = qt.QShortcut(qt.QKeySequence('.'), mainWindow())<br />
s.connect('activated()', printDataProbe)<br />
</pre><br />
<br />
== Thick slab reconstruction and maximum/minimum intensity volume projections ==<br />
<br />
Set up 'red' slice viewer to show thick slab reconstructed from 3 slices:<br />
<pre><br />
sliceNode = slicer.mrmlScene.GetNodeByID('vtkMRMLSliceNodeRed')<br />
appLogic = slicer.app.applicationLogic()<br />
sliceLogic = appLogic.GetSliceLogic(sliceNode)<br />
sliceLayerLogic = sliceLogic.GetBackgroundLayer()<br />
reslice = sliceLayerLogic.GetReslice()<br />
reslice.SetSlabModeToMean()<br />
reslice.SetSlabNumberOfSlices(10) # mean of 10 slices will computed<br />
reslice.SetSlabSliceSpacingFraction(0.3) # spacing between each slice is 0.3 pixel (total 10 * 0.3 = 3 pixel neighborhood)<br />
sliceNode.Modified()<br />
</pre><br />
<br />
Set up 'red' slice viewer to show maximum intensity projection (MIP):<br />
<pre><br />
sliceNode = slicer.mrmlScene.GetNodeByID('vtkMRMLSliceNodeRed')<br />
appLogic = slicer.app.applicationLogic()<br />
sliceLogic = appLogic.GetSliceLogic(sliceNode)<br />
sliceLayerLogic = sliceLogic.GetBackgroundLayer()<br />
reslice = sliceLayerLogic.GetReslice()<br />
reslice.SetSlabModeToMax()<br />
reslice.SetSlabNumberOfSlices(600) # use a large number of slices (600) to cover the entire volume<br />
reslice.SetSlabSliceSpacingFraction(0.5) # spacing between slices are 0.5 pixel (supersampling is useful to reduce interpolation artifacts)<br />
sliceNode.Modified()<br />
</pre><br />
<br />
The projected image is available in a ''vtkImageData'' object by calling ''reslice.GetOutput()''.<br />
<br />
== Change default file type for nodes (that have never been saved yet) ==<br />
Default node can be specified that will be used as a basis of all new storage nodes. This can be used for setting default file extension. For example, change file format to STL for model nodes:<br />
<pre><br />
msn=slicer.vtkMRMLModelStorageNode()<br />
msn.SetDefaultWriteFileExtension('stl')<br />
slicer.mrmlScene.AddDefaultNode(msn)<br />
</pre><br />
<br />
== Change file type for saving for all volumes (with already existing storage nodes) ==<br />
<pre><br />
requiredFileExtension = '.nia'<br />
originalFileExtension = '.nrrd'<br />
volumeNodes = slicer.mrmlScene.GetNodesByClass('vtkMRMLScalarVolumeNode')<br />
volumeNodes.UnRegister(slicer.mrmlScene)<br />
volumeNodes.InitTraversal()<br />
volumeNode = volumeNodes.GetNextItemAsObject()<br />
while volumeNode:<br />
volumeStorageNode = volumeNode.GetStorageNode()<br />
if not volumeStorageNode:<br />
volumeStorageNode = volumeNode.CreateDefaultStorageNode()<br />
slicer.mrmlScene.AddNode(volumeStorageNode)<br />
volumeStorageNode.UnRegister(None)<br />
volumeNode.SetAndObserveStorageNodeID(volumeStorageNode.GetID())<br />
volumeStorageNode.SetFileName(volumeNode.GetName()+requiredFileExtension)<br />
else:<br />
volumeStorageNode.SetFileName(volumeStorageNode.GetFileName().replace(originalFileExtension,requiredFileExtension))<br />
volumeNode = volumeNodes.GetNextItemAsObject()<br />
</pre><br />
<br />
== Segmentations ==<br />
<br />
=== Create a segmentation from a labelmap volume and display in 3D ===<br />
<br />
<pre><br />
labelmapVolumeNode = getNode('label')<br />
seg = slicer.mrmlScene.AddNewNodeByClass('vtkMRMLSegmentationNode')<br />
slicer.modules.segmentations.logic().ImportLabelmapToSegmentationNode(labelmapVolumeNode, seg)<br />
seg.CreateClosedSurfaceRepresentation()<br />
slicer.mrmlScene.RemoveNode(labelmapVolumeNode)<br />
</pre><br />
<br />
The last line is optional. It removes the original labelmap volume so that the same information is not shown twice.<br />
<br />
=== Show a segmentation in 3D ===<br />
Segmentation can only be shown in 3D if closed surface representation (or other 3D-displayable representation) is available. To create closed surface representation:<br />
<pre><br />
segmentation.CreateClosedSurfaceRepresentation()<br />
</pre><br />
<br />
=== Get a representation of a segment ===<br />
Get binary labelmap, if it does not exist, it will return None:<br />
<pre><br />
image = segmentationNode.GetBinaryLabelmapRepresentation(segmentID)<br />
</pre><br />
Get closed surface, if it does not exist, it will return None:<br />
<pre><br />
polydata = segmentationNode.GetClosedSurfaceRepresentation(segmentID)<br />
</pre><br />
Get binary labelmap representation. If it does not exist then it will be created for that single segment. Applies parent transforms by default (if not desired, another argument needs to be added to the end: false):<br />
<pre><br />
import vtkSegmentationCorePython as vtkSegmentationCore<br />
outputOrientedImageData = vtkSegmentationCore.vtkOrientedImageData()<br />
slicer.vtkSlicerSegmentationsModuleLogic.GetSegmentBinaryLabelmapRepresentation(segmentationNode, segmentID, outputOrientedImageData)<br />
</pre><br />
Same as above, for closed surface representation:<br />
<pre><br />
outputPolyData = vtk.vtkPolyData()<br />
slicer.vtkSlicerSegmentationsModuleLogic.GetSegmentClosedSurfaceRepresentation(segmentationNode, segmentID, outputPolyData)<br />
</pre><br />
<br />
=== Convert all segments using default path and conversion parameters ===<br />
<pre><br />
segmentationNode.CreateBinaryLabelmapRepresentation()<br />
</pre><br />
<br />
=== Convert all segments using custom path or conversion parameters ===<br />
Change reference image geometry parameter based on an existing referenceImageData image:<br />
<pre><br />
import vtkSegmentationCorePython as vtkSegmentationCore<br />
referenceGeometry = vtkSegmentationCore.vtkSegmentationConverter.SerializeImageGeometry(referenceImageData)<br />
segmentation.SetConversionParameter(vtkSegmentationCore.vtkSegmentationConverter.GetReferenceImageGeometryParameterName(), referenceGeometry)<br />
</pre><br />
<br />
=== Re-convert using a modified conversion parameter ===<br />
Changing smoothing factor for closed surface generation:<br />
<pre><br />
import vtkSegmentationCorePython as vtkSegmentationCore<br />
segmentation = getNode('Segmentation').GetSegmentation()<br />
<br />
# Turn of surface smoothing<br />
segmentation.SetConversionParameter('Smoothing factor','0.0')<br />
<br />
# Recreate representation using modified parameters (and default conversion path)<br />
segmentation.RemoveRepresentation(vtkSegmentationCore.vtkSegmentationConverter.GetSegmentationClosedSurfaceRepresentationName())<br />
segmentation.CreateRepresentation(vtkSegmentationCore.vtkSegmentationConverter.GetSegmentationClosedSurfaceRepresentationName())<br />
</pre><br />
<br />
=== How to run segment editor effects from a script ===<br />
<br />
Editor effects are complex because they need to handle changing master volumes, undo/redo, masking operations, etc. Therefore, instead of using a segment editor effect, it is simpler to run the underlying filters directly from script.<br />
<br />
This example demonstrates how to run grow from seeds effect in batch mode (without GUI, using qMRMLSegmentEditorWidget):<br />
https://subversion.assembla.com/svn/slicerrt/trunk/SlicerRt/samples/PythonScripts/SegmentGrowCut/SegmentGrowCutSimple.py<br />
<br />
This example demonstrates how to perform grow from seeds using VTK filters:<br />
https://subversion.assembla.com/svn/slicerrt/trunk/SlicerRt/samples/PythonScripts/SegmentGrowCut/SegmentGrowCut.py<br />
<br />
== Accessing views, renderers, and cameras ==<br />
<br />
Iterate through all 3D views in current layout:<br />
<br />
<pre><br />
layoutManager = slicer.app.layoutManager()<br />
for threeDViewIndex in range(layoutManager.threeDViewCount) :<br />
view = layoutManager.threeDWidget(threeDViewIndex).threeDView()<br />
threeDViewNode = view.mrmlViewNode()<br />
cameraNode = slicer.modules.cameras.logic().GetViewActiveCameraNode(threeDViewNode)<br />
print('View node for 3D widget ' + str(threeDViewIndex))<br />
print(' Name: ' + threeDViewNode .GetName())<br />
print(' ID: ' + threeDViewNode .GetID())<br />
print(' Camera ID: ' + cameraNode.GetID())<br />
</pre><br />
<br />
Iterate through all slice views in current layout:<br />
<br />
<pre><br />
layoutManager = slicer.app.layoutManager()<br />
for sliceViewName in layoutManager.sliceViewNames():<br />
view = layoutManager.sliceWidget(sliceViewName).sliceView()<br />
sliceNode = view.mrmlSliceNode()<br />
sliceLogic = slicer.app.applicationLogic().GetSliceLogic(sliceNode)<br />
compositeNode = sliceLogic.GetSliceCompositeNode()<br />
print('Slice view ' + str(sliceViewName))<br />
print(' Name: ' + sliceNode.GetName())<br />
print(' ID: ' + sliceNode.GetID())<br />
print(' Background volume: {0}'.format(compositeNode.GetBackgroundVolumeID()))<br />
print(' Foreground volume: {0} (opacity: {1})'.format(compositeNode.GetForegroundVolumeID(), compositeNode.GetForegroundOpacity()))<br />
print(' Label volume: {0} (opacity: {1})'.format(compositeNode.GetLabelVolumeID(), compositeNode.GetLabelOpacity()))<br />
</pre><br />
<br />
For low-level manipulation of views, it is possible to access VTK render windows, renderers and cameras of views in the current layout.<br />
<pre><br />
renderWindow = view.renderWindow()<br />
renderers = renderWindow.GetRenderers()<br />
renderer = renderers.GetItemAsObject(0)<br />
camera = cameraNode.GetCamera()<br />
</pre><br />
<br />
== Subject hierarchy == <br />
==== Get the pseudo-singleton subject hierarchy node ====<br />
It manages the whole hierarchy and provides functions to access and manipulate<br />
shNode = slicer.vtkMRMLSubjectHierarchyNode.GetSubjectHierarchyNode(slicer.mrmlScene)<br />
<br />
==== Create subject hierarchy item ====<br />
# If it is for a data node, it is automatically created, but the create function can be used to set parent:<br />
shNode.CreateItem(parentItemID, dataNode)<br />
# If it is a hierarchy item without a data node, then the create function must be used:<br />
shNode.CreateSubjectItem(parentItemID, name)<br />
shNode.CreateFolderItem(parentItemID, name)<br />
shNode.CreateHierarchyItem(parentItemID, name, level) # Advanced method to set level attribute manually (usually subject, study, or folder, but it can be a virtual branch for example)<br />
<br />
==== Get subject hierarchy item ====<br />
Items in subject hierarchy are uniquely identified by integer IDs<br />
# Get scene item ID first because it is the root item:<br />
sceneItemID = shNode.GetSceneItemID()<br />
# Get direct child by name<br />
subjectItemID = shNode.GetItemChildWithName(sceneItemID, 'Subject_1')<br />
# Get item for data node<br />
itemID = shNode.GetItemByDataNode(dataNode)<br />
# Get item by UID (such as DICOM)<br />
itemID = shNode.GetItemByUID(slicer.vtkMRMLSubjectHierarchyConstants.GetDICOMUIDName(), seriesInstanceUid)<br />
itemID = shNode.GetItemByUIDList(slicer.vtkMRMLSubjectHierarchyConstants.GetDICOMInstanceUIDName(), instanceUID)<br />
# Invalid item ID for checking validity of a given ID (most functions return the invalid ID when item is not found)<br />
invalidItemID = slicer.vtkMRMLSubjectHierarchyNode.GetInvalidItemID()<br />
<br />
==== Traverse children of a subject hierarchy item ====<br />
children = vtk.vtkIdList()<br />
shNode.GetItemChildren(parent, children)<br />
for i in xrange(children.GetNumberOfIds()):<br />
child = children.GetId(i)<br />
...<br />
<br />
==== Manipulate subject hierarchy item ====<br />
Instead of node operations on the individual subject hierarchy nodes, item operations are performed on the one subject hierarchy node.<br />
# Set item name<br />
shNode.SetItemName(itemID, 'NewName')<br />
# Set item parent (reparent)<br />
shNode.SetItemParent(itemID, newParentItemID)<br />
# Set visibility of data nodes associated to items in a branch (or a leaf item)<br />
shNode.SetDisplayVisibilityForBranch(itemID, 1)<br />
<br />
==== Filter nodes in TreeView or ComboBox ====<br />
Subject hierarchy module [https://github.com/Slicer/Slicer/tree/master/Modules/Loadable/SubjectHierarchy/Widgets/DesignerPlugins provides] Qt Designer plugins for qMRMLSubjectHierarchyTreeView or qMRMLSubjectHierarchyComboBox.<br />
TreeView can be used to display subject hierarchy and ComboBox to select nodes from the hierarchy. Displayed nodes can be filtered using ''setAttributeFilter'' method. An example of the usage can be found in the [https://github.com/Slicer/Slicer/blob/e66e3b08e35384526528e6ae678e9ec9f079f286/Applications/SlicerApp/Testing/Python/SubjectHierarchyGenericSelfTest.py#L352-L360 unit test]. Modified version here:<br />
print shTreeView.displayedItemCount() # 5<br />
shTreeView.setAttributeFilter('DICOM.Modality') # Nodes must have this attribute<br />
print shTreeView.displayedItemCount() # 3<br />
shTreeView.setAttributeFilter('DICOM.Modality','CT') # Have attribute and equal 'CT'<br />
print shTreeView.displayedItemCount() # 1<br />
shTreeView.removeAttributeFilter()<br />
print shTreeView.displayedItemCount() # 5</div>Dzenanzhttps://www.slicer.org/w/index.php?title=Documentation/Nightly/Developers/Tutorials/Debugging_Python_in_Visual_Studio&diff=52097Documentation/Nightly/Developers/Tutorials/Debugging Python in Visual Studio2017-06-02T18:05:20Z<p>Dzenanz: /* Setup and configuration */ VS2017</p>
<hr />
<div><noinclude>{{documentation/versioncheck}}</noinclude><br />
<br />
==Python Tools for Visual Studio==<br />
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.<br />
<br />
==Setup and configuration==<br />
* 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. "Python language support" is part of Visual Studio 2017, and can be added through the installer.<br />
* Optional: configure Slicer's Python environment in Visual Studio. This enables IntelliSense specific to this Python environment.<br />
** Select ''View > Other Windows > Python Environments'' to show the Python Environments window.<br />
** Click "''+ Custom''"<br />
** 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.<br />
** Click ''Apply'' and make sure that the new environment is selected at the top of the window.<br />
<br />
==Install remote debugging server==<br />
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:<br />
* Install pip if necessary: <tt>./Slicer --launch "C:\D\S4D\python-install\Scripts\easy_install.exe" pip</tt><br />
* Install ptvsd using pip: <tt>./Slicer --launch "C:\D\S4D\python-install\Scripts\pip.exe" install ptvsd</tt><br />
<br />
==Configure the remote debugging server==<br />
The PTVS page provides detailed [https://github.com/Microsoft/PTVS/wiki/Cross-Platform-Remote-Debugging remote debugging instructions]. In brief, the steps are:<br />
* In Visual Studio, open the script—the Slicer scripted module—to debug.<br />
* Add the following code to the script to enable remote debugging:<br />
<pre><br />
import ptvsd<br />
ptvsd.enable_attach(secret='slicer')<br />
ptvsd.wait_for_attach()<br />
</pre><br />
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>.<br />
<br />
Calling <tt>wait_for_attach()</tt> blocks Slicer until the debugger attaches.<br />
<br />
Alternatively, this code could be added to <tt>~/.slicerrc.py</tt> so that it's executed when Slicer starts.<br />
<br />
==Attach the debugger==<br />
* Start Slicer.<br />
* In Visual Studio, select ''Debug > Attach to Process'' to display the ''Attach to Process'' window, then:<br />
** Choose ''Python remote (ptvsd)'' as the ''Transport''.<br />
** Enter "''tcp://slicer@localhost''" as the ''Qualifier''. Here, "''slicer''" should match the <tt>secret</tt> specified in <tt>enable_attach()</tt>.<br />
** Press Enter or click ''Refresh''. The Slicer process should appear in the list below. Select the Slicer process and click ''Attach''.<br />
<br />
Once the debugger attaches, you can use breakpoints, step through the code, and examine variables in the Watch window.<br />
<br />
==Notes==<br />
* It might be necessary to configure Windows Firewall to allow the Slicer executable to listen on port 5678.<br />
* Debugging Slicer's C++ code and Python code simultaneously is possible by using separate instances of Visual Studio.</div>Dzenanzhttps://www.slicer.org/w/index.php?title=Documentation/Nightly/Developers/Build_Instructions/Package&diff=49720Documentation/Nightly/Developers/Build Instructions/Package2017-02-03T16:16:07Z<p>Dzenanz: /* PACKAGE Slicer */ CMakePredefinedTargets group</p>
<hr />
<div><noinclude>{{documentation/versioncheck}}</noinclude><br />
== PACKAGE Slicer ==<br />
<br />
{|width = "100%"<br />
! width="50%" style="border-bottom: 1px solid darkgrey;font-size: 75%;"| Linux or MacOSX (Makefile)<br />
! width="50%" style="border-bottom: 1px solid darkgrey;font-size: 75%;"| Windows (Visual Studio)<br />
|-<br />
| valign="top" |<br />
Start a terminal.<br />
{{pre2|scroll|<br />
$ cd ~/Projects/Slicer-SuperBuild<br />
$ cd Slicer-build<br />
<br />
$ make package}}<br />
| valign="top" |<br />
<small>'''Note:''' Make sure to patch VS2008 using the script [https://gist.github.com/jcfr/3c7bef3f8b32f9f6ad4b fix-vc9-redist-manifests.ps1]</small><br><br />
Start Windows Explorer. [http://www.wikihow.com/Open-Windows-Explorer Need help?]<br />
<ol start="1" style="list-style-type: decimal;"><br />
<li>Open <code>Slicer-SuperBuild\Slicer-build\Slicer.sln</code> in Visual Studio</li><br />
<li>Select '''Release''' build configuration.</li><br />
<li>In the Solution Explorer, right click on '''PACKAGE''' project in group '''CMakePredefinedTargets''' and then select '''Build'''</li><br />
</ol><br />
<small>Note: '''PACKAGE''' project can be found in the '''CMakePredefinedTargets''' folder. See [http://slicer-devel.65872.n3.nabble.com/Easier-development-using-Visual-Studio-with-introduction-of-target-folders-td4033427.html here] for more details.</small><br />
<br />
[[File:Slicer-vs2013-CMakePredefinedTargets-PACKAGE.png]]<br />
<br />
|}</div>Dzenanzhttps://www.slicer.org/w/index.php?title=Documentation/Nightly/Developers/Build_Instructions/Prerequisites&diff=48972Documentation/Nightly/Developers/Build Instructions/Prerequisites2016-12-05T17:30:20Z<p>Dzenanz: /* Common Prerequisites */ 3.7.0-rc2 -> 3.7.1</p>
<hr />
<div><noinclude>{{documentation/versioncheck}}</noinclude><br />
__TOC__<br />
<br />
== PREREQUISITES ==<br />
<br><br />
<!--<br />
Please check that the following tools are installed on your machine.<br />
<br />
We try to keep current with the most recent releases of these prerequisites, but sometimes it's critical to use the exact versions specified here. If you run into issues please do whatever you can to find a combination that works or contact the developer mailing list for suggestions.<br />
<br />
<br><br />
{{mbox<br />
| type = protection<br />
| text = Qt libraries are '''required'''. Consider reading [[Documentation/{{documentation/version}}/Developers/Build_Instructions/Prerequisites/Qt|Qt requirements]].<br />
| image= [[{{tool|logo|qt}}|x40px]]<br />
}}<br />
<br><br />
<br />
{{mbox<br />
| type = protection<br />
| text = CMake is '''required'''.<br />
| image= [[{{tool|logo|cmake}}|x40px]]<br />
}}<br />
<br><br />
<br />
{{mbox<br />
| type = protection<br />
| text = &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Git is '''required'''.<br />
| image= [[{{tool|logo|git}}|x40px]]<br />
}}<br />
<br><br />
<br />
{{mbox<br />
| type = protection<br />
| text = SVN is '''required'''.<br />
| image= [[{{tool|logo|svn}}|x40px]]<br />
}}<br />
<br />
<br><br />
--><br />
The prerequisites listed below are required to be able to configure/build/package/test Slicer.<br />
<br />
=== Linux ===<br />
* [{{tool|download|cmake}} CMake] >= 3.7-rc02<br />
* [{{tool|download|git}} Git] >= 1.7.10<br />
* [{{tool|download|svn}} Svn] >= 1.7<br />
* Qt 4.8.6. <small>See details [[Documentation/{{documentation/version}}/Developers/Build_Instructions/Prerequisites/Qt#Linux|here]]. See some history [http://na-mic.org/Mantis/view.php?id=3325#bugnotes here] (Note that any version >= Qt 4.7.4 can be used only for Ubuntu < 12.04)</small><br />
<br />
==== Debian ====<br />
<br />
* Debian squeeze/wheezy/testing(jessie) users, start by pasting the following lines in a terminal<br />
sudo apt-get install subversion git-core git-svn<br />
sudo apt-get install make gcc g++ libx11-dev libxt-dev libgl1-mesa-dev libosmesa6-dev libglu1-mesa-dev libfontconfig-dev libxrender-dev libncurses5-dev<br />
sudo apt-get install cmake<br />
sudo apt-get install qt-sdk<br />
<br />
<hr><br />
<br />
<br />
==== Ubuntu ====<br />
<br />
===== Common Prerequisites =====<br />
<br />
<br />
sudo apt-get install subversion git-core git-svn<br />
<br />
sudo apt-get install make gcc g++ libx11-dev libxt-dev libgl1-mesa-dev libglu1-mesa-dev libfontconfig-dev libxrender-dev libncurses5-dev<br />
<br />
sudo apt-get install libosmesa6-dev # Only for Ubuntu < 14.04.3<br />
<br />
{{remark|red|On Ubuntu 14.04.3 LTS, attempting to install <tt>libosmesa6-dev</tt> results in an error.<pre><br />
The following packages have unmet dependencies:<br />
libosmesa6-dev : Depends: libosmesa6 (= 10.1.3-0ubuntu0.4) but it is not going to be installed<br />
E: Unable to correct problems, you have held broken packages.<br />
</pre><br />
For more details, see [https://bugs.launchpad.net/ubuntu/+source/mesa-lts-utopic/+bug/1424059 Bug 1424059].}}<br />
<br />
{{remark|green|Slicer compiles successfully without that package, but <tt>VTK_OPENGL_HAS_OSMESA</tt> is disabled.}}<br />
<br />
===== CMake =====<br />
<br />
<ol><br />
<li>Open a terminal and copy the command reported below</li><br />
<li>Download stable version of CMake and extract the archive:<br />
<pre><br />
mkdir ~/Support && cd ~/Support<br />
curl -O https://cmake.org/files/v3.5/cmake-3.5.2-Linux-x86_64.tar.gz<br />
tar -xzvf cmake-3.5.2-Linux-x86_64.tar.gz<br />
</pre><br />
</li><br />
<li>Create symbolic links into <code>~/bin</code><br />
<pre><br />
mkdir -p ~/bin<br />
for name in cmake ctest cpack ccmake cmake-gui; do<br />
ln -s ~/Support/cmake-3.5.2-Linux-x86_64/bin/$name ~/bin/$name<br />
done<br />
</pre><br />
</li><br />
</ol><br />
<br />
{{remark|red|You <b>MUST</b> download the standard CMake binary because the distributed version of CMake cannot be used to build slicer.<br><br />
See [[Documentation/{{documentation/version}}/Developers/FAQ/Building#Why_distributed_CMake_can_not_be_used_on_Ubuntu_12.04_and_above_.3F|here]] for more details.}}<br />
<br />
===== Qt =====<br />
<br />
'''Ubuntu 14.04 and above''': Qt 4 == 4.8.7 MUST be used.<br />
<br />
See https://github.com/jcfr/qt-easy-build/blob/4.8.7/Build-qt.txt<br />
<br />
<br />
<br />
'''Ubuntu 12.04, 12.10, 13.04 and 13.10''': Qt 4 >= 4.8.5 MUST be used.<br />
<br />
<pre><br />
cd ~/Support # This is where we will build Qt and dependent libraries<br />
<br />
# Keep track of our working directory<br />
cwd=$(pwd)<br />
<br />
# This will download, then build zlib and openssl in the current folder<br />
rm -f get-and-build-openssl-for-slicer.sh<br />
wget https://gist.githubusercontent.com/jcfr/9513568/raw/21f4e4cabca5ad03435ecc17ab546dab5e2c1a2f/get-and-build-openssl-for-slicer.sh<br />
chmod u+x get-and-build-openssl-for-slicer.sh <br />
./get-and-build-openssl-for-slicer.sh <br />
<br />
# This will download Qt source in the current folder<br />
wget http://packages.kitware.com/download/item/6175/qt-everywhere-opensource-src-4.8.6.tar.gz<br />
md5=`md5sum ./qt-everywhere-opensource-src-4.8.6.tar.gz | awk '{ print $1 }'` &&<br />
[ $md5 == "2edbe4d6c2eff33ef91732602f3518eb" ] || echo "MD5 mismatch. Problem downloading Qt"<br />
<br />
# This will configure and build Qt in RELEASE against the zlib and openssl previously built<br />
tar -xzvf qt-everywhere-opensource-src-4.8.6.tar.gz<br />
mv qt-everywhere-opensource-src-4.8.6 qt-everywhere-opensource-release-src-4.8.6<br />
mkdir qt-everywhere-opensource-release-build-4.8.6<br />
cd qt-everywhere-opensource-release-src-4.8.6<br />
./configure -prefix $cwd/qt-everywhere-opensource-release-build-4.8.6 \<br />
-release \<br />
-opensource -confirm-license \<br />
-no-qt3support \<br />
-webkit \<br />
-nomake examples -nomake demos \<br />
-openssl -I $cwd/openssl-1.0.1e/include -L $cwd/openssl-1.0.1e \<br />
&& make -j7 && make install<br />
</pre><br />
<br />
For more details, consider reading the FAQ entry: [[Documentation/{{documentation/version}}/Developers/FAQ/Building#Why_Qt_4_.3E.3D_4.8.5_should_be_used_on_Ubuntu_12.04_and_above_.3F|Why Qt 4 >= 4.8.5 should be used on Ubuntu 12.04 and above ?]]<br />
<br />
<hr><br />
<br />
<br />
==== CentOS ====<br />
*CentOS user type:<br />
yum install make gcc-c++ libX11-devel libXt-devel libXrender-devel libXext-devel libGLU-devel mesa-libOSMesa-devel mesa-libGL-devel mesa-libGLU-devel ncurses<br />
<br />
<!--<br />
Todo: This will have to be added in FAQ: Troubleshoot section<br />
''glx-utils'' provides ''glxgears'' that can be used to test rendering<br />
--><br />
<br />
=== MacOSX ===<br />
<br />
* El Capitan is what most developers use.<br />
* CMake 3.7-rc02 is recommended.<br />
<br />
====MacOSX 10.9.4 (Mavericks)====<br />
<br />
''' (1) Make sure to install this update: http://support.apple.com/kb/DL1754'''<br />
<br />
''' (2) Use CMake 3.0.1 - it is known to be working and is supported''' (if you want to use CMake already installed on your system, 2.8.12.2 is known to work on Mac OS X 10.9.5)<br />
<br />
* Mac Os X >= 10.5 (Leopard)<br />
* [{{tool|download|cmake}} CMake] >= 2.8.9<br />
** For Mac Os X >= 10.8 ([http://en.wikipedia.org/wiki/OS_X_Mountain_Lion Mountain Lion]) and/or recent XCode >= 4.5.X - [{{tool|download|cmake}} CMake] >= 2.8.11 is required. See http://www.cmake.org/files/v2.8/cmake-2.8.11-Darwin64-universal.tar.gz<br />
<!-- Waiting for the official release, get the release candidate rc1 [http://www.cmake.org/files/v2.8/cmake-2.8.11-rc1-Darwin64-universal.tar.gz here]. For explanation, see [[Documentation/{{documentation/version}}/Developers/Build_Instructions#ld:_framework_not_found_QtWebKit|here]] and [[Documentation/{{documentation/version}}/Developers/Build_Instructions#On_MacOSX_10.8.2C_CMake_hangs_forever|here]]. These versions are also known to work: exact version 20130121-g92bd8 [http://www.cmake.org/files/dev/cmake-2.8.10.20130121-g92bd8-Darwin-universal.tar.gz here] (or version >= 2.8.10.20130220 [http://www.cmake.org/files/dev/?C=M;O=D here]).<br />
--><br />
<br />
$ curl -O http://www.cmake.org/files/v2.8/cmake-2.8.11-Darwin64-universal.tar.gz<br />
$ tar -xzvf cmake-2.8.11-Darwin64-universal.tar.gz --strip-components=1<br />
<br />
$ CMake\ 2.8-11.app/Contents/bin/cmake --version<br />
cmake version 2.8.11<br />
<br />
* [{{tool|download|git}} Git] >= 1.7.10<br />
* [{{tool|download|svn}} Svn] >= 1.7<br />
<!--<br />
*[[{{tool|logo|cmake}}|x16px]] [{{tool|download|cmake}} CMake] >= 2.8.9<br />
*[[{{tool|logo|git}}|x16px]] [{{tool|download|git}} Git] >= 1.6.5<br />
--><br />
* XCode (for the SDK libs)<br />
** After installing XCode, install XCode command line developer tools: <br />
<pre><br />
xcode-select --install<br />
</pre><br />
* Qt 4 >= 4.8.5. We recommend you install the following two packages:<br />
** Download and install [http://download.qt-project.org/official_releases/qt/4.8/4.8.6/qt-opensource-mac-4.8.6-1.dmg qt-opensource-mac-4.8.6-1.dmg]<br />
** Download and install [http://download.qt-project.org/official_releases/qt/4.8/4.8.6/qt-opensource-mac-4.8.6-1-debug-libs.dmg qt-opensource-mac-4.8.6-1-debug-libs.dmg]<br />
** For more details [[Documentation/{{documentation/version}}/Developers/Build_Instructions/Prerequisites/Qt#Mac|here]]<br />
* XQuartz - For Mac Os X >= 10.8 ([http://en.wikipedia.org/wiki/OS_X_Mountain_Lion Mountain Lion]) install XQuartz (http://xquartz.macosforge.org) to get X11 (no longer a default in OS X).<br />
<!--<br />
** Newer Xcode versions (e.g. 4.3.2) use clang as the default compiler and '''clang is not compatible with ITK version 3'''. You should use ITK version 4 with recent versions of Xcode.<br />
** Xcode with gcc should ork with either version of ITK.<br />
** OS X Mountain Lion: In Xcode 4.5 you now need to install command line tools (no longer included by default). Install within Xcode under the Xcode->Preferences->Downloads tab (otherwise git svn will give errors). Then you will need to install XQuartz (http://xquartz.macosforge.org) to get X11 (no longer a default in OS X).<br />
--><br />
<br />
====Mac OSX 10.11 (El Capitan)====<br />
<br />
XCode up to version 7 is known to work for Slicer compilation. XCode 8 breaks things on several levels for now. <br />
Remember to install XCode command line tools (see above) if a reinstall for XCode is needed. <br />
<br />
The standard Qt4 installers fail on this version and the 4.8.6 source code won't build. But [http://slicer-devel.65872.n3.nabble.com/incompatible-qt-4-8-6-with-OS-X-El-Capitan-td4035551.html as described on the slicer-devel mailing list] it is possible to install the homebrew version of qt4.<br />
<br />
After installing the OS and Xcode, run the following two commands:<br />
<pre><br />
brew install qt4<br />
xcode-select --install<br />
</pre><br />
<br />
TCL does not build correctly on El Capitan as of 2015-12-03, so when building Slicer turn Slicer_USE_PYTHONQT_WITH_TCL off.<br />
<br />
==== Mac OSX 10.12 (Sierra) ====<br />
<br />
Follow instructions for 10.11 and then run :<br />
<pre><br />
mkdir Slicer-superbuild<br />
cd Slicer-superbuild<br />
cmake -DCMAKE_OSX_DEPLOYMENT_TARGET:STRING=10.9 -DSlicer_USE_PYTHONQT_WITH_TCL:BOOL=OFF ../Slicer<br />
make -j `sysctl -n hw.ncpu`<br />
</pre><br />
<br />
Note: if Xcode is installed and the compiler works but you get errors about stdlib.h be sure you have run the '''xcode-select --install''' step described above.<br />
<br />
<br />
Confirmed with Xcode: Version 8.1 (8B62) and cmake version 3.4.20151021-g8fbc8e<br />
<br />
=== Windows ===<br />
<br />
==== Common Prerequisites ====<br />
* [{{tool|download|cmake}} CMake] >= 3.0 (3.7.1 recommended)<br />
* [{{tool|download|git}} Git] >= 1.7.10<br />
<!--<br />
*[[{{tool|logo|cmake}}|x16px]] [{{tool|download|cmake}} CMake] >= 2.8.10<br />
*[[{{tool|logo|git}}|x16px]] [{{tool|download|git}} Git] >= 1.7.10<br />
* [https://code.google.com/p/msysgit/downloads/list?can=3 ''Git-X.X.X-preview2013XXXX.exe''] recommended.<br />
--><br />
<!--<br />
** Use of [http://code.google.com/p/tortoisegit/ TortoiseGit] is optional.<br />
--><br />
** {{note}}For convenience, you could update the PATH variable so that ''git'' can be automatically discovered when configuring Slicer. If not, you will have to specify the GIT_EXECUTABLE at configure time.<br />
<br />
* [http://www.sliksvn.com/en/download SlikSvn] <!-- or [http://www.cygwin.com cygwin's svn client]-->. If using TortoiseSVN (versions that support command line tools), make sure you install Command line tools (disabled by default)<br />
<br />
* NSIS (optional): Needed if packaging Slicer - Click [http://nsis.sourceforge.net/Download here] to download.<br />
<br />
* Qt: See details [[Documentation/{{documentation/version}}/Developers/Build_Instructions/Prerequisites/Qt#Windows|here]].<br />
<br />
==== Tested and recommended build environment ====<br />
<br />
* [https://www.visualstudio.com/en-us/news/vs2013-community-vs.aspx Visual Studio 2013] with Update 5, as of [https://github.com/Slicer/Slicer/commit/71602609ce2ae5e473ece392500825ca96213371 commit 7160260]: Slicer compilation is successful, with some caveats.<br />
** CMake >= 3.3.1<br />
** Build Qt 4.8.7 with SSL support using [https://github.com/jcfr/qt-easy-build/tree/4.8.7 qt-easy-build].<br />
** For Debug mode, disable <code>Slicer_USE_SimpleITK</code> in CMake.<br />
<br />
==== Experimental/deprecated build environments ====<br />
<br />
* VS2008 (deprecated)<br />
** [https://www.microsoft.com/visualstudio/en-us/products/2008-editions Microsoft Visual Studio 9 2008] (Any edition). [http://go.microsoft.com/?linkid=7729279 VS Express 2008 with SP1 direct link] (functional as of Jan 11, 2014)<br />
** Make sure to install SP1: Click [http://www.microsoft.com/download/en/details.aspx?id=10986 here] to download SP1 and click [http://massmail.spl.harvard.edu/public-archives/slicer-devel/2012/008206.html here] to understand the motivation.<br />
*** Make sure to patch VS2008 using the script [https://gist.github.com/jcfr/3c7bef3f8b32f9f6ad4b fix-vc9-redist-manifests.ps1]<br />
* VS2010 (experimental, may not work)<br />
** [https://www.microsoft.com/visualstudio/en-us/products/2010-editions Microsoft Visual Studio 2010] (Any edition)<br />
** Make sure to build or install:<br />
*** Qt 4.8 <br />
** Make sure to install:<br />
*** SP1. Click [http://www.microsoft.com/download/en/details.aspx?id=23691 here] to download SP1 and click [http://developer.qt.nokia.com/faq/answer/why_do_all_qt_4.7_applications_crash_when_using_windows_7_x64_w_vs_2010 here] to understand the motivation.<br />
*** SP1 Compiler update. Click [https://www.microsoft.com/download/en/details.aspx?id=4422 here] to download and [http://slicer-devel.65872.n3.nabble.com/Windows-7-64Bits-Slicer-4-Build-with-VC-2010-problem-tt3730524.html#a3731661 here] to understand the motivation.<br />
<!-- JC: This links shouldn't be here: 1) Since this the developer section, it's assumed Visual Studio is installed. 2) The Dll are distributed within Slicer package --><br />
<!-- *** Optional: "Microsoft Visual C++ 2010 SP1 Redistributable Package". Click [http://www.microsoft.com/download/en/details.aspx?id=13523 here for x64] download, or [http://www.microsoft.com/download/en/details.aspx?id=13523 here for x86] download. [http://www.microsoft.com/download/en/details.aspx?displaylang=en&id=29 x86 (32bit) for VS 2008] --><br />
* VS2012 (experimental)<br />
** Use a desktop version such as [http://www.microsoft.com/visualstudio/eng/downloads#d-express-windows-desktop Visual Studio 2012 Express <b>for Windows Desktop</b>] and remember [http://www.microsoft.com/en-us/download/details.aspx?id=38188 Visual Studio 2012 Update 2]). Do <b>not</b> use Visual Studio Express 2012 <em>for Windows 8</em>. You must install [http://msdn.microsoft.com/en-us/library/ms717422.aspx Windows SDK], otherwise CMake will not even find the C compiler during configure. In CMake, choose <tt>Visual Studio 11 Win64</tt> as generator.<br />
* Cygwin (untested)<br />
** Cygwin suite (building with cygwin gcc not supported, but the cygwin shell environment can be used to run git, svn, etc).</div>Dzenanzhttps://www.slicer.org/w/index.php?title=Documentation/Nightly/Developers/SlicerExecutionModel&diff=47385Documentation/Nightly/Developers/SlicerExecutionModel2016-10-31T15:51:17Z<p>Dzenanz: /* XML Schema */ Adding transform</p>
<hr />
<div>== Introduction ==<br />
<br />
The Slicer Execution Model is designed to improve the acceptance and productivity of Slicer application developers. The Execution Model provides a simple mechanism for incorporating command line programs as Slicer modules. These command line modules are self-describing, emitting an XML description of its command line arguments. Slicer uses this XML description to construct a GUI for the module.<br />
<br />
=== Types of Slicer Plugins ===<br />
<br />
There are various types of plugins that Slicer supports as command line modules. This variety allows a breadth of integration choices to balance performance and flexibility.<br />
<br />
==== Shared object plugins (dll, so) with global symbols ====<br />
<br />
Shared object plugins with global symbols integrate into Slicer tighter than the Executable plugins. Shared object plugins with global symbols can transfer data directly to/from a MRML scene using standard itk::ImageFileReader and itk::ImageFileWriter (and the ImageIO class provided with Slicer, itk::MRMLIDImageIO). Communicating directly with the MRML scene avoids the overhead of reading and writing images to disk. Slicer looks for a standard entry point to execute the module called ModuleEntryPoint defined as<br />
<br />
int ModuleEntryPoint(int argc, char* argv[]);<br />
<br />
Slicer also looks for the global symbols XMLModuleDescription, ModuleLogoImage, ModuleLogoWidth, ModuleLogoHeight, ModuleLogoPixelSize, and ModuleLogoLength. These global symbols provide the xml module description and data for the module logo. The data types for these symbols are<br />
<br />
char *XMLModuleDescription;<br />
unsigned char *ModuleLogoImage;<br />
int ModuleLogoWidth;<br />
int ModuleLogoHeight;<br />
int ModuleLogoPixelSize;<br />
unsigned long ModuleLogoLength;<br />
<br />
These global symbols are accessed during module discovery. The ModuleLogoImage, ModuleLogoWidth, ModuleLogoHeight, ModuleLogoPixelSize, and ModuleLogoLength are optional.<br />
<br />
=====Controlling what symbols are exported from a shared library module=====<br />
<br />
Shared Object Modules are loaded into Slicer using standard "dlopen" style calls. The ModuleEntryPoint is located within the shared library and cached for later use. The ModuleEntryPoint, however, may not be the only function accessible to Slicer within the shared library. To protect against symbol clash at runtime, all functions and variables accessible within file scope of the shared library should be put in an anonymous namespace.<br />
<br />
<pre><br />
// Use an anonymous namespace to keep class types and function names<br />
// from colliding when module is used as shared object module. Every<br />
// thing should be in an anonymous namespace except for the module<br />
// entry point, e.g. main() or ModuleEntryPoint()<br />
//<br />
namespace {<br />
// functions like DoIt() should be put in the anonymous namespace<br />
template<class T> int DoIt( int argc, char * argv[], const T& targ)<br />
{<br />
}<br />
} // end of anonymous namespace<br />
<br />
<br />
int main(int argc, char* argv[] )<br />
{<br />
}<br />
<br />
</pre><br />
<br />
==== Shared object plugins (dll, so) with entry points ====<br />
<br />
Shared object plugins with entry points integrate into Slicer tighter than the Executable plugins. Shared object plugins with entry points can transfer data directly to/from a MRML scene using standard itk::ImageFileReader and itk::ImageFileWriter (and the ImageIO class provided with Slicer, itk::MRMLIDImageIO). Communicating directly with the MRML scene avoids the overhead of reading and writing images to disk. Slicer looks for standard entry points for executing the module as well as for querying the module for its xml description and logos. The entry points are defined as<br />
<br />
int ModuleEntryPoint(int argc, char* argv[]);<br />
char *GetXMLModuleDescription();<br />
unsigned char *GetModuleLogo()(int *width, int *height, int *pixel_size, unsigned long *bufferLength);<br />
<br />
GetXMLModuleDescription() and GetModuleLogo() are accessed during module discovery. GetModuleLogo() is optional.<br />
<br />
==== Executable plugins with global symbols ====<br />
<br />
Executable plugins with global symbols allow for a single executable to be developed that can be integrated into Slicer 3 or run standalone on a cluster. Plugins of this type are opened but not executed at module discovery time. Slicer 3 looks for the global symbols XMLModuleDescription, ModuleLogoImage, ModuleLogoWidth, ModuleLogoHeight, ModuleLogoPixelSize, and ModuleLogoLength. The data types for these symbols are<br />
<br />
char *XMLModuleDescription;<br />
unsigned char *ModuleLogoImage;<br />
int ModuleLogoWidth;<br />
int ModuleLogoHeight;<br />
int ModuleLogoPixelSize;<br />
unsigned long ModuleLogoLength;<br />
<br />
These global symbols are access during module discovery. ModuleLogoImage, ModuleLogoWidth, ModuleLogoHeight, ModuleLogoPixelSize, ModuleLogoLength are optional.<br />
<br />
==== Executable plugins (exe) with command line options ====<br />
<br />
Executable plugins with command line options are the most flexible type of plugin. This approach allows for legacy applications to be integrated into Slicer using a wrapper around the legacy application. Plugins of this type are executed at module discovery time, passing in the command line argument "--xml". The plugin responds to the "--xml" query by emitting the xml description of the module. The plugin is also executed at module discovery time with a "--logo" command line argument. The plugin responds to the "--logo" query by emitting a logo description. <br />
<br />
This type of plugin allows for legacy applications to be integrated into Slicer. A developer can provide Slicer with a small executable or shell script that responds to the "--xml" and "--logo" command line arguments needed for Slicer 3 integration and otherwise spawns the legacy executable passing down any command line arguments.<br />
<br />
> module.exe --xml<br />
> module.exe --logo<br />
<br />
==== Script plugins with Python ====<br />
<br />
Python scripts are found using the discovery mechanisms for the other plugins. Since Python plugins are fundamentally different from the shared and executable plugins, they are documented on their own [[Documentation/Nightly/Developers/SlicerExecutionModel/Python|page]].<br />
<br />
=== Calling Command Line Modules from Other Code ===<br />
<br />
In addition to the automated GUI that shows up in the Slicer user interface, the Execution Model can be used to encapsulate functionality that is invoked by other parts of the program. For instance, the Editor Module can invoke the Model Maker Module to build models. By invoking a command line module in this way, you get the advantage of background processing with progress reporting. See [[Documentation/Nightly/Developers/SlicerExecutionModel/Programmatic Invocation|this page]] for more details.<br />
<br />
=== Architecture === <br />
[[Image:ExecutionModelPlugins.png|600px|Plugin architecture]]<br />
[[Image:CommandLineModule.png|400px|Module architecture]]<br />
[[Image:ModuleFactory.png|400px|Module factory ]]<br />
[[Image:Class_parser_state_coll_graph.png|400px|Module description]]<br />
<br />
== Module Description ==<br />
<br />
Modules are described using XML. The XML is used to generate the C++ command line code and the GUI for the application.<br />
<br />
=== XML Schema ===<br />
<br />
At a minimum, each module description must contain:<br />
<br />
<?xml version="1.0" encoding="utf-8"?><br />
<executable><br />
<title>A title</title><br />
<description>A description</description><br />
<parameters><br />
At least one parameter<br />
</parameters><br />
</executable><br />
<br />
In the following descriptions of each XML tag, CLP means command line processing and GUI means graphical user interface. Unless otherwise specified, tags are optional.<br />
<br />
; <executable> (required)<br />
<br />
;; <category><br />
:: Classifies the executable (e.g. Filtering, Segmentation). Category can be a ''dot'' separated string. Multiple categories can be given and should be separated by a ''semicolon''.<br />
:: ''for CLP'', not used.<br />
:: ''for GUI'', used on the menu selector to group executables. ''Dot'' separated strings can be used to generate sub-menus. ''Semicolon'' separated strings can be used to create multiple categories.<br />
;; </category><br />
<br />
;; <title> (required)<br />
:: A word or two describing the executable (e.g. Median Filter, Anisotropic Diffusion<br />
:: ''for CLP'', not used.<br />
:: ''for GUI'', used to label the frame containing the GUI for the executable. Also, GUI names for volumes use this label as a prefix.<br />
;; </title><br />
<br />
;; <description> (required)<br />
:: A long description of the executable. Any double quotes will be converted to single quotes.<br />
:: ''for CLP'', appears at the end of the --help.<br />
:: ''for GUI'', appears in the help frame.<br />
;; </description><br />
<br />
;; <version><br />
:: The version of the command line executable. A suggested format is:<br />
::: ''major''.''minor''.''patch''.''build''.''status''<br />
::: where status is<br />
:::: vc: version controlled (pre-alpha), build can be a serial revision number, if any (like svn might have).<br />
:::: a: alpha<br />
:::: b: beta<br />
:::: rc: release candidate<br />
:::: fcs: first customer ship<br />
:: ''for CLP'', reported in response to --version.<br />
:: ''for GUI'', not used.<br />
;; </version><br />
<br />
;; <documentation-url><br />
:: The location of extended documentation for the executable, (e.g. http://www.na-mic.org/foo.html).<br />
;; </documentation-url><br />
<br />
;; <license><br />
:: The type of license or a url containing the license, (e.g. Berkeley, Apache, http://www.slicer.org/copyright/copyright.txt).<br />
:: ''for CLP'', not used.<br />
:: ''for GUI'', may show up in the Help or About section.<br />
;; </license><br />
<br />
;; <contributor><br />
:: The author(s) of the command line executable (e.g. Pieper, Jim Miller).<br />
:: for ''CLP'', appears as part of --help<br />
:: for ''GUI'', may show up in the Help or About section.<br />
;; </contributor><br />
<br />
;; <acknowledgements><br />
:: Acknowledgements for funding agency, employer, colleague, (e.g. This work is part of the National Alliance for Medical Image Computing NAMIC), funded by the National Institutes of Health through the NIH Roadmap for Medical Research, Grant U54 EB005149).<br />
:: for ''CLP'', appears as part of --help<br />
:: for ''GUI'', may show up in the Help of About section.<br />
;; </acknowledgements><br />
<br />
;; <parameters [advanced="true|''false''"]> (required for each group of parameters)<br />
:: Starts a group of parameters.<br />
:: for ''CLP'', not used.<br />
:: for ''GUI'', defines a widget (in tk, a frame) that contains other widgets. If ''advanced'' is true, the frame will be closed initially.<br />
<br />
;;; <label> (required)<br />
::: A short string that summarizes a parameter group, (e.g. I/O, Diffusion)<br />
::: for ''CLP'', not used.<br />
::: for ''GUI'', used to label the frame.<br />
;;; </label><br />
<br />
;;; <description> (required)<br />
::: A short description of the parameter group, (e.g. Input/Output Parameters, Anitostropic Diffusion Parameters). Any double quotes will be converted to single quotes.<br />
::: ''for CLP'', not used.<br />
::: ''for GUI'', used in balloon help for the frame containing the parameter group.<br />
;;; </description><br />
<br />
;;; <integer> | <float> | <double> | <boolean> | <string> | <integer-vector> | <float-vector> | <double-vector> | <string-vector> | <integer-enumeration> | <float-enumeration> | <double-enumeration> | <string-enumeration> | <file> | <directory> | <image [type="''scalar''|''label''|''tensor''|''diffusion-weighted''|''vector''|''model''"]> | <geometry [type="''fiberbundle''|''model''"]> | <point [multiple="''true''|''false''"] [coordinateSystem="''lps''|''ras''|''ijk''"]> | <pointfile [multiple="''true''|''false''"] [coordinateSystem="''lps''|''ras''|''ijk''"]> | <region [multiple="''true''|''false''"] [coordinateSystem="''lps''|''ras''|''ijk''"]> | &lt;table [type="''color''"] [fileExtensions="''.tsv''|''.csv''|''.txt''|''.ctbl''"]> | <transform fileExtensions=".tfm,.h5,.hdf5,.mat,.txt"><br />
::: The type of the parameter. <br />
:::: The scalar types ('''integer''', '''float''', etc.) correspond to the usual programming language types. <br />
:::: The '''-vector''' types are represented by comma separated values of the scalar type. <br />
:::: The '''-enumeration''' types use the '''<element>''' tag to enumerate choices of the scalar type. <br />
:::: '''<image>''' is a special type that indicates that the parameter is a file name that specifies images. <br />
:::: If the attribute ''multiple'' is "true", multiple arguments are allowed for '''scalar''', '''file''', '''directory''', '''image''', '''geometry''', '''point''', '''pointfile''' and '''region''' parameters. BUG: the automatically built GUI will not support selecting multiple volumes for the '''image''' argument, but they can be passed on the command line. If the parameter has a ''flag'' or ''longflag'', then the flag may be specified multiple times on the command line. The resulting C++ variable will be a std::vector of the scalar type. If the multiple parameter does not have a flag, then multiple arguments can appear on the command line. However, a multiple parameter with no flags must be the last parameter specified.<br />
:::: The attribute ''coordinateSystem'' is allowed for the parameters '''point''', '''pointfile''' and '''region'''. <br />
:::: The attribute ''fileExtensions'' is allowed for '''file''', '''pointfile''', '''image''', '''transform''' and '''geometry'''. fileExtensions can contain a list of comma separated file extensions for optional use by the GUI.<br />
<br />
;;;; <name> (required if longflag is not specified)<br />
:::: The name of a command line argument. If name is not specified, longflag will be used (e.g. conductance, numberOfIterations). The name must be usable as a C++ variable. For example, it CANNOT have spaces or special characters and must start with a letter.<br />
:::: ''for CLP'', the name of the C++ variable.<br />
:::: ''for GUI'', used internally.<br />
;;;; </name><br />
<br />
;;;; <description> (required)<br />
:::: A brief description of the parameter. Any double quotes will be converted to single quotes.<br />
:::: ''for CLP'', describes the parameter for --usage and --help.<br />
:::: ''for GUI'', describes the parameter when the cursor is placed over the widget for the parameter (balloon help).<br />
;;;; </description><br />
<br />
;;;; <label> (required)<br />
:::: A label for parameter (e.g. Dicom Directory, Conductance).<br />
:::: ''for'' CLP, not used.<br />
:::: ''for'' GUI, the label for the parameter widget.<br />
;;;; </label><br />
<br />
;;;; <default><br />
:::: A default value for the parameter. The default must be a type that is compatible with the parameter type. The vector parameters are specified as comma separated values of the atomic parameter type.<br />
:::: ''for CLP'', contains the default for the parameter unless the parameter is a ''boolean''. The default for ''boolean'' parameters is always set to ''false''.<br />
:::: ''for GUI'', contains the default for the parameter.<br />
;;;; </default><br />
<br />
;;;; <flag [alias="a,b"] [deprecatedalias="c,d"]> (not required if longflag is present)<br />
:::: A single character command line flag (e.g. s, W). Can provide "alias"'s (comma separated) if different flags can be used to activate the same parameter. Can provide "deprecatedalias"'s (comma separated) if different flags can be used to set the same parameter but the user should be notified of which "updated" flag to use. Parameters with flags are considered "optional" and do not have be specified or assigned. Parameters with flags allow one to override a default behavior.<br />
:::: ''for CLP'', used as the short flag on the command line.<br />
:::: ''for GUI'', used when running the module.<br />
;;;; </flag><br />
<br />
;;;; <longflag [alias="foo,bar"] [deprecatedalias="garf"]> (not required if flag is present)<br />
:::: A command line flag (e.g. spacing, Watcher). Can provide "alias"'s (comma separated) if different long flags can be used to activate the same parameter. Can provide "deprecatedalias"'s (comma separated) if different long flags can be used to set the same parameter but the user should be notified of which "updated" long flag to use. Parameters with flags are considered "optional" and do not have be specified or assigned. Parameters with flags allow one to override a default behavior.<br />
:::: Note: apparently you can't use hyphens in the longflag, so things like --my-option are not allows. -gcs<br />
:::: ''for CLP'', used as the long flag on the command line.<br />
:::: ''for GUI'', used when running the module.<br />
;;;; </longflag><br />
<br />
;;;; <constraints><br />
:::: Encloses constraints on the value of a non-vector, non-enumerated parameter.<br />
:::: ''for CLP'', not used.<br />
:::: ''for GUI'', if present, a slider will be created using the minimum, maximum and step specified.<br />
<br />
;;;;; <minimum><br />
::::: The minimum allowed value for the parameter. If not specified, the minimum is the smallest possible value for the parameter type.<br />
;;;;; </minimum><br />
<br />
;;;;; <maximum><br />
::::: The maximum allowed value for the parameter. If not specified, the maximum is the largest possible value for the parameter type.<br />
;;;;; </maximum><br />
<br />
;;;;; <step><br />
::::: The increment for the parameter.<br />
;;;;; </step><br />
<br />
;;;; </constraints><br />
<br />
;;;; <channel> (required for file, pointfile, directory and image parameters)<br />
:::: Specifies whether the parameter is an input or output parameter.<br />
:::: ''for CLP'', not used.<br />
:::: ''for GUI'', selects the proper widget for file handling.<br />
;;;; </channel><br />
<br />
;;;; <index> (required if there are no flags specified)<br />
:::: An integer starting at 0, that specifies a command line argument that has no flags.<br />
:::: Note: if you use index for, say, an image, the user must enter some input value into the GUI. If the user does not fill in a value, the plugin is not run at all. However, slicer will "seem" to run it, and no error message is given. -gcs<br />
:::: ''for CLP'', specifies the order of an argument that has no flags.<br />
:::: ''for GUI'', used when running the module.<br />
;;;; </index><br />
<br />
;;;; <enumeration> (required for enumeration parameters)<br />
:::: Encloses elements for the parameter. The parameter is restricted one and only one element.<br />
:::: ''for CLP'', not used.<br />
:::: ''for GUI'', defines a radio button with choices.<br />
<br />
;;;;; <element><br />
::::: Defines the choice. Must be of the proper type for a parameter.<br />
::::: ''for CLP'', not used.<br />
::::: ''for GUI'', used as the label for the raido button.<br />
;;;;; </element><br />
<br />
;;;; </enumeration><br />
<br />
;;; </integer> | </float> | </double> | </boolean> | </string> | </integer-vector> | </float-vector> | </double-vector> | </string-vector> | </integer-enumeration> | </float-enumeration> | </double-enumeration> | </string-enumeration> | </file> | </directory> | </image> | </geometry> | </point> | </pointfile> | </region> | &lt;/table> | </transform><br />
;; </parameters><br />
<br />
; </executable><br />
<br />
== Slicer GUI Generation ==<br />
<br />
Slicer generates GUI's for each executable discovered during the startup process. Slicer searches directories stored in the Slicer Module Path. This path is set from the Slicer application in View->Application Settings->Modules->Additional module paths. Slicer attempts to run every executable in the prescribed directories and look for a valid XML file in response to a "--xml" command line.<br />
<br />
Here are a few representative examples.<br />
<br />
=== A tour of the Execution Model XML ===<br />
<br />
This example is a sampler of the parameters available in the Execution Model.<br />
<br />
<div class="floatright"><span>[[Image:ExectionModelTourGUI.png|[[Image:ExectionModelTourGUI.png]]]]</span></div><br />
<br />
<?xml version="1.0" encoding="utf-8"?><br />
<executable><br />
<category>Tours</category><br />
<title>Execution Model Tour</title><br />
<description><br />
Shows one of each type of parameter.<br />
</description><br />
<version>1.0</version><br />
<documentation-url></documentation-url><br />
<license></license><br />
<contributor>Daniel Blezek</contributor><br />
<br />
<parameters><br />
<label>Scalar Parameters</label><br />
<description><br />
Variations on scalar parameters<br />
</description><br />
<integer><br />
<name>integerVariable</name><br />
<flag>i</flag><br />
<longflag>integer</longflag><br />
<description><br />
An integer without constraints<br />
</description><br />
<label>Integer Parameter</label><br />
<default>30</default><br />
</integer><br />
<label>Scalar Parameters With Constraints</label><br />
<description>Variations on scalar parameters</description><br />
<double><br />
<name>doubleVariable</name><br />
<flag>d</flag><br />
<longflag>double</longflag><br />
<description>An double with constraints</description><br />
<label>Double Parameter</label><br />
<default>30</default><br />
<constraints><br />
<minimum>0</minimum><br />
<maximum>1.e3</maximum><br />
<step>0</step><br />
</constraints><br />
</double><br />
</parameters><br />
<br />
<parameters><br />
<label>Vector Parameters</label><br />
<description>Variations on vector parameters</description><br />
<float-vector><br />
<name>floatVector</name><br />
<flag>f</flag><br />
<description>A vector of floats</description><br />
<label>Float Vector Parameter</label><br />
<default>1.3,2,-14</default><br />
</float-vector><br />
<string-vector><br />
<name>stringVector</name><br />
<longflag>string_vector</longflag><br />
<description>A vector of strings</description><br />
<label>String Vector Parameter</label><br />
<default>"foo",bar,"foobar"</default><br />
</string-vector><br />
</parameters><br />
<br />
<parameters><br />
<label>Enumeration Parameters</label><br />
<description>Variations on enumeration parameters</description><br />
<string-enumeration><br />
<name>stringChoice</name><br />
<flag>e</flag><br />
<longflag>enumeration</longflag><br />
<description>An enumeration of strings</description><br />
<label>String Enumeration Parameter</label><br />
<default>foo</default><br />
<element>foo</element><br />
<element>"foobar"</element><br />
<element>foofoo</element><br />
</string-enumeration><br />
</parameters><br />
</executable><br />
<br />
=== Module with an integer-vector, one input image and one output image ===<br />
<br />
Here is the XML that describes the MedianImageFilter. The image on the right shows the generated Slicer 3 GUI. The help frame has been expanded by the user.<br />
<br />
<div class="floatright"><span>[[Image:MedianFilterGUI.png|[[Image:MedianFilterGUI.png]]]]</span></div><br />
<pre><br />
<?xml version="1.0" encoding="utf-8"?><br />
<executable><br />
<category><br />
Filtering.Denoising<br />
</category><br />
<title><br />
Median Filter<br />
</title><br />
<description><br />
The MedianImageFilter is commonly used as a robust approach for<br />
noise reduction. This filter is particularly efficient against<br />
"salt-and-pepper" noise. In other words, it is robust to the presence<br />
of gray-level outliers. MedianImageFilter computes the value of each output<br />
pixel as the statistical median of the neighborhood of values around the<br />
corresponding input pixel.<br />
</description><br />
<version>0.1.0.$Revision: 2085 $(alpha)</version><br />
<documentation-url></documentation-url><br />
<license></license><br />
<contributor>Bill Lorensen</contributor><br />
<acknowledgements>This command module was derived from<br />
Insight/Examples/Filtering/MedianImageFilter (copyright) Insight Software Consortium<br />
</acknowledgements><br />
<parameters><br />
<label>Median Filter Parameters</label><br />
<description>Parameters for the median filter</description><br />
<br />
<integer-vector><br />
<name>neighborhood</name><br />
<longflag>--neighborhood</longflag><br />
<description>The size of the neighborhood in each dimension</description><br />
<label>Neighborhood Size</label><br />
<default>1,1,1</default><br />
</integer-vector><br />
<br />
</parameters><br />
<br />
<parameters><br />
<label>IO</label><br />
<description>Input/output parameters</description><br />
<image><br />
<name>inputVolume</name><br />
<label>Input Volume</label><br />
<channel>input</channel><br />
<index>0</index><br />
<description>Input volume to be filtered</description><br />
</image><br />
<image><br />
<name>outputVolume</name><br />
<label>Output Volume</label><br />
<channel>output</channel><br />
<index>1</index><br />
<description>Output filtered</description><br />
</image><br />
</parameters><br />
<br />
</executable><br />
</pre><br />
<br />
=== Module with a multiple scalars, one Input image and one output image ===<br />
<br />
A module with<br />
<br />
<div class="floatright"><span>[[Image:AnisotropicDiffusionFilterGUI.png|[[Image:AnisotropicDiffusionFilterGUI.png]]]]</span></div><br />
<br />
<?xml version="1.0" encoding="utf-8"?><br />
<executable><br />
<category>filtering</category><br />
<title>Anisotropic Diffusion</title><br />
<description><br />
Runs anisotropic diffusion on a volume<br />
</description><br />
<version>1.0</version><br />
<documentation-url></documentation-url><br />
<license></license><br />
<contributor>Bill Lorensen</contributor><br />
<br />
<parameters><br />
<label><br />
Anisotropic Diffusion Parameters<br />
</label><br />
<description><br />
Parameters for the anisotropic<br />
diffusion algorithm<br />
</description><br />
<br />
<double><br />
<name>conductance</name><br />
<longflag>conductance</longflag><br />
<description>Conductance</description><br />
<label>Conductance</label><br />
<default>1</default><br />
<constraints><br />
<minimum>0</minimum><br />
<maximum>10</maximum><br />
<step>.01</step><br />
</constraints><br />
</double><br />
<br />
<double><br />
<name>timeStep</name><br />
<longflag>timeStep</longflag><br />
<description>Time Step</description><br />
<label>Time Step</label><br />
<default>0.0625</default><br />
<constraints><br />
<minimum>.001</minimum><br />
<maximum>1</maximum><br />
<step>.001</step><br />
</constraints><br />
</double><br />
<br />
<integer><br />
<name>numberOfIterations</name><br />
<longflag>iterations</longflag><br />
<description>Number of iterations</description><br />
<label>Iterations</label><br />
<default>1</default><br />
<constraints><br />
<minimum>1</minimum><br />
<maximum>30</maximum><br />
<step>1</step><br />
</constraints><br />
</integer><br />
<br />
</parameters><br />
<br />
<parameters><br />
<label>IO</label><br />
<description>Input/output parameters</description><br />
<image><br />
<name>inputVolume</name><br />
<label>Input Volume</label><br />
<channel>input</channel><br />
<index>0</index><br />
<description>Input volume to be filtered</description><br />
</image><br />
<image><br />
<name>outputVolume</name><br />
<label>Output Volume</label><br />
<channel>output</channel><br />
<index>1</index><br />
<description>Output filtered</description><br />
</image><br />
</parameters><br />
<br />
</executable><br />
<br />
<br />
=== Module with output text presented in GUI ===<br />
<br />
Slicer auto generates GUI to display output from the command line module after execution is complete.<br />
The flag [--returnparameterfile <file name>] is used to provide a name of a file that the module uses to store key=value pairs that are displayed in the Slicer GUI. The keys are described in the command line module xml schema using the output channel.<br />
<br />
This example demonstrates a portion of an XML schema containing 1 string-enumeration input and 7 string outputs.<br />
<br />
<div class="floatright"><span>[[Image:RadnosticsBetaOsteoporosisAnalysisGUI.png|[[Image:RadnosticsBetaOsteoporosisAnalysisGUI.png]]]]</span></div><br />
<br />
<parameters><br />
<label>Radnostics Beta Osteoporosis Analysis</label><br />
<string-enumeration><br />
<name>Osteoporosis</name><br />
<longflag>--osteoporosis</longflag><br />
<label>After Segmentation Perform Osteoporosis Analysis</label><br />
<description>Osteoporosis Analysis will provide a Radnostics Osteoporosis score for the patient.</description><br />
<default>yes</default><br />
<element>yes</element><br />
<element>no</element><br />
</string-enumeration><br />
<string><br />
<name>PatientGender</name><br />
<label>Gender</label><br />
<channel>output</channel><br />
<description>Patient Gender</description> <br />
<default>na</default><br />
</string> <br />
<string><br />
<name>PatientAge</name><br />
<label>Age</label><br />
<channel>output</channel><br />
<description>Patient Age</description> <br />
<default>na</default><br />
</string> <br />
<string><br />
<name>RadnosticsOsteoporosisScore</name><br />
<label>Radnostics Osteoporosis Score</label><br />
<channel>output</channel><br />
<description>Radnostics Osteoporosis Score</description> <br />
<default>na</default><br />
</string> <br />
<string><br />
<name>OsteoporosisComment1</name><br />
<channel>output</channel><br />
<label>*</label><br />
<description>Comment 1</description><br />
</string><br />
<string><br />
<name>OsteoporosisComment2</name><br />
<channel>output</channel><br />
<label>*</label><br />
<description>Comment 2</description><br />
</string><br />
<string><br />
<name>OsteoporosisComment3</name><br />
<channel>output</channel><br />
<label>*</label><br />
<description>Comment 3</description><br />
</string><br />
<string><br />
<name>OsteoporosisComment4</name><br />
<channel>output</channel><br />
<label>*</label><br />
<description>Comment 4</description><br />
</string><br />
</parameters><br />
<br />
The command line module outputs key=value pairs to the file supplied by –-returnparameterfile as shown below:<br />
PatientGender = F <br />
PatientAge = 059Y<br />
RadnosticsOsteoporosisScore = 8 (high)<br />
OsteoporosisComment1 = vertebra label 58 has biconcave fracture<br />
OsteoporosisComment2 = vertebra label 56 has mean density of 120 HU<br />
<br />
<br />
<br />
== Command Line Parsing ==<br />
<br />
The Slicer Execution Model has support for parsing executable command lines. The C++ code to parse the command line arguments is generated automatically from the same XML description that generates the GUI. ''GenerateCLP,'' located in ''SlicerExecutionModel/GenerateCLP'' reads the XML Module Description and creates an include file ''"Executable"CLP.h'' in the build tree. The executable includes this header file and accesses the code with the macro PARSE_ARGS.<br />
<br />
GenerateCLP provides the following to the executable:<br />
<br />
# A brief usage command if required arguments are missing<br />
# A full help command if ''-h'' or ''--help'' is specified on the command line<br />
# A copy of the xml description if ''--xml'' is specified on the command line<br />
# An echo of the command line parameters and their values if ''--echo'' is specified<br />
<br />
GenerateCLP provides the following source code:<br />
<br />
# A C++ declaration of the proper type for each parameter assigning the default value if specified by the XML<br />
# For ''-vector'' parameters, a ''std::vector'' containing the proper C++ type fo the parameter. The generated code parses the comma separated strings to generate the ''std::vector''<br />
<br />
=== Using GenerateCLP ===<br />
<br />
GenerateCLP is normally used via CMake where it is implemented as a CUSTOM_COMMAND. To use GenerateCLP from CMake, you must first include the GenerateCLP package from your CMakeLists.txt file, which will make some macros available to you:<br />
<br />
find_package(SlicerExecutionModel REQUIRED)<br />
include(${SlicerExecutionModel_USE_FILE})<br />
<br />
Note that the HelloSlicer command line module example provides a good starting point and can be used as a skeleton to build your own. It can be found in the [http://na-mic.org/ViewVC/index.cgi/trunk/Modules/CommandLineModule/Testing/HelloSlicer/?sortby=file#dirlist Modules/CommandLineModule/Testing/HelloSlicer] subdirectory.<br />
<br />
For each executable, include the following, replacing '''MyFilter''' with the name of your C++ source:<br />
<br />
set('''MyFilter'''_SOURCE '''MyFilter'''.cxx)<br />
GENERATECLP('''MyFilter'''_SOURCE '''MyFilter'''.xml)<br />
<br />
To generate a stand-alone executable add the lines:<br />
<br />
add_executable('''MyFilter''' ${'''MyFilter'''_SOURCE})<br />
target_link_libraries('''MyFilter''' ${ITK_LIBRARIES})<br />
<br />
Slicer expects modules and plugins to be stored in a specific subdirectory, so that they can be discovered and loaded at run-time. To make sure your CLP module is built in said subdirectory, add the line:<br />
<br />
SEMMacroBuildCLI(<br />
NAME '''MyFilter'''<br />
LOGO_HEADER ${Slicer_SOURCE_DIR}/Resources/NAMICLogo.h<br />
ADDITIONAL_SRCS<br />
'''MyFilter'''.cxx<br />
TARGET_LIBRARIES ModuleDescriptionParser ${ITK_LIBRARIES} vtkTeem MRMLCore SlicerBaseCLI ${VTK_LIBRARIES}<br />
INCLUDE_DIRECTORIES<br />
${vtkTeem_INCLUDE_DIRS}<br />
${MRMLCore_INCLUDE_DIRS}<br />
${vtkITK_INCLUDE_DIRS}<br />
${SlicerBaseCLI_SOURCE_DIR} ${SlicerBaseCLI_BINARY_DIR}<br />
)<br />
<br />
The add_executable target creates a stand-alone executable that can be run from a command line. The add_library target creates a shared library that is discovered at Slicer 3 startup.<br />
<br />
Although this example linked to ITK libraries, other libraries can be specified.<br />
<br />
=== Short Example ===<br />
<br />
This example uses the XML for the [http://www.slicer.org/slicerWiki/index.php/Slicer3:Execution_Model_Documentation#Module_with_an_integer-vector.2C_one_input_image_and_one_output_image|Median Image Filter example].<br />
<br><br />
'''Note:''' The program '''MUST NOT''' write anything to stdout before the ''PARSE_ARGS'' statement. If something is written, the plugin discovery mechanism will not recognize the program as a plugin.<br />
<br />
<nowiki>#include "MedianImageFilterCLP.h"<br />
int main (int argc, char * argv[])<br />
{<br />
PARSE_ARGS;<br />
std::cout << "The size of the neighborhood is: " << neighborhood.size()<br />
<< " and the first element of the neighborhood is: " << neighborhood[0]<br />
<< std::endl;<br />
std::cout << "The input volume is: " << inputVolume << std::endl;<br />
std::cout << "The output volume is: " << outputVolume << std::endl;<br />
return EXIT_SUCCESS;<br />
}<br />
</nowiki><br />
<br />
Here is the output --help:<br />
<br />
<pre><br />
<br />
USAGE: <br />
<br />
./MedianImageFilter [--processinformationaddress <std::string>] [--xml]<br />
[--echo] [--neighborhood <std::vector<int>>] [--]<br />
[--version] [-h] <std::string> <std::string><br />
<br />
<br />
Where: <br />
<br />
--processinformationaddress <std::string><br />
Address of a structure to store process information (progress, abort,<br />
etc.). (default: 0)<br />
<br />
--xml<br />
Produce xml description of command line arguments (default: 0)<br />
<br />
--echo<br />
Echo the command line arguments (default: 0)<br />
<br />
--neighborhood <std::vector<int>><br />
The size of the neighborhood in each dimension (default: 1,1,1)<br />
<br />
--, --ignore_rest<br />
Ignores the rest of the labeled arguments following this flag.<br />
<br />
--version<br />
Displays version information and exits.<br />
<br />
-h, --help<br />
Displays usage information and exits.<br />
<br />
<std::string><br />
(required) Input volume to be filtered<br />
<br />
<std::string><br />
(required) Output filtered<br />
<br />
<br />
The MedianImageFilter is commonly used as a robust approach for noise<br />
reduction. This filter is particularly efficient against<br />
'salt-and-pepper' noise. In other words, it is robust to the presence of<br />
gray-level outliers. MedianImageFilter computes the value of each output<br />
pixel as the statistical median of the neighborhood of values around the<br />
corresponding input pixel.<br />
<br />
Author(s): Bill Lorensen<br />
<br />
Acknowledgements: This command module was derived from<br />
Insight/Examples/Filtering/MedianImageFilter (copyright) Insight<br />
Software Consortium<br />
<br />
<br />
</pre><br />
<br />
=== Parameters and C++ code ===<br />
<br />
This table shows how parameters are defined in the C++ code and how they are specified on the command line.<br />
<br />
{| border="1"<br />
! XML<br />
! C++ Declaration<br />
! Command Line<br />
|-<br />
|<br />
<integer><br /> <name>count</name><br /> <flag>c</flag> </integer><br />
|<br />
int count;<br />
|<br />
''prog'' -c 10<br />
|-<br />
|<br />
<float><br /> <name>stepSize</name><br /> <default>.0625</default><br /> <longflag>stepSize</longflag><br /> </float><br />
|<br />
float stepSize=.0625;<br />
|<br />
''prog'' --stepSize .003<br />
|-<br />
|<br />
<integer multiple="true"><br /> <name>iterations</name><br /> <flag>i</flag><br /> <default>100</default><br /> </integer><br />
|<br />
std::vector<int> iterations;<br /> iterations.push_back(100);<br /><br />
|<br />
''prog'' -i 20 -i 30 -i 100<br />
|-<br />
|<br />
<float-vector><br /> <name>variation</name><br /> <flag>v</flag><br /> <default>1,2,3</default><br /> </float-vector><br />
|<br />
std::vector<float> variation; iterations.push_back(1);<br /> iterations.push_back(2);<br /> iterations.push_back(3);<br /><br />
|<br />
''prog'' -v 10,20,3<br />
|-<br />
|<br />
<string-vector><br /> <name>sites</name><br /> <longflag>sites</longflag> </string-vector><br />
|<br />
std::vector<std::string> sites;<br />
|<br />
''prog'' --sites BWH,GE,Kitware,UNC,MIT,UTAH,GT<br />
|-<br />
|<br />
<string-enumeration><br /> <name>leaders</name><br /> <default>Bill</default><br /> <element>Ron</element><br /> <element>Bill</element><br /> <element>Steve</element><br /> </string-enumeration><br />
|<br />
std::string leaders = "Bill";<br />
|<br />
''prog'' --leaders Ron<br />
|-<br />
|<br />
<boolean><br /> <name>debugSwitch</name><br /> <flag>d</flag> <default>true</default><br /> </boolean><br />
|<br />
bool debugSwitch = true;<br />
|<br />
''prog'' -d<br />
|-<br />
|<br />
<file><br /> <longflag>file1</longflag><br /> <file><br />
|<br />
std::string file1;<br />
|<br />
''prog'' --file1 mytext.txt<br />
|-<br />
|<br />
<image><br /> <name>image</name><br /> <index>0</index><br /> </image><br />
|<br />
std::string image;<br />
|<br />
''prog'' c:/lorensen/Data/ct.nrrd<br />
|-<br />
|<br />
<file multiple="true"><br /> <name>args</name><br /> <index>1</index><br /> </file><br />
|<br />
std::vector<std::string> args;<br />
|<br />
''prog'' --otherFlags file1 file2 ... filen<br />
|-<br />
|<br />
<point multiple="true" coordinateSystem="ras"><br /> <name>seed</name><br /> <longflag>--seed</longflag><br />
|<br />
std::vector<std::vector<float> > seed;<br />
|<br />
''prog'' --seed 10,100,23 --seed 5,240,17<br />
|}<br />
<br />
== Accessing Module Information at Runtime ==<br />
<br />
All of the information contained in the XML description of a module can be accessed at run-time by the command line program. The ''ModuleDescriptionParser'' class library can parse an XML module description and populate a ''ModuleDescription'' instance.<br />
<pre><br />
// Module Description Parser Class Library<br />
#include "ModuleDescriptionParser.h"<br />
#include "ModuleDescription.h"<br />
#include "ModuleParameterGroup.h"<br />
#include "ModuleParameter.h"<br />
.<br />
.<br />
.<br />
// Create a module and a parser<br />
ModuleDescription module;<br />
ModuleDescriptionParser parser;<br />
// Parse the XML<br />
if (parser.Parse(GetXMLModuleDescription(), module))<br />
{<br />
std::cerr << argv[0] << ": One or more XML errors detected." << std::endl;<br />
return EXIT_FAILURE;<br />
}<br />
// Access the module description information<br />
std::cout << "Module Description Information" << std::endl;<br />
std::cout << "\tCategory is: " << module.GetCategory() << std::endl;<br />
std::cout << "\tTitle is: " << module.GetTitle() << std::endl;<br />
std::cout << "\tDescription is: " << module.GetDescription() << std::endl;<br />
std::cout << "\tVersion is: " << module.GetVersion() << std::endl;<br />
std::cout << "\tDocumentationURL is: " << module.GetDocumentationURL() << std::endl;<br />
std::cout << "\tLicense is: " << module.GetLicense() << std::endl;<br />
std::cout << "\tContributor is: " << module.GetContributor() << std::endl;<br />
</pre><br />
<br />
''GetXMLModuleDescription'' is automatically generated by ''GenerateCLP''. Information about parameter groups and parameters is also available [[Accessing_Module_Information_at_Runtime|here]].<br />
<br />
The ''CMakeLists.txt'' file that creates the command line module should point to the ''ModuleDescriptionParser'' library.<br />
<pre><br />
target_link_libraries(${CLP}<br />
ModuleDescriptionParser<br />
)<br />
</pre><br />
<br />
== Error Handling ==<br />
<br />
GenerateCLP attempts to do error checking so that the generated C++ code will compile. These errors will show up as custom command errors during the build process.<br />
<br />
* XML Errors<br />
** ''mismatched tag at line xx'' : The closing tag (a tag with </ >) does not have a matching opening tag.<br />
** ''not well-formed (invalid token) at line xx'' : Probably a blank in the token name.<br />
* ModuleDescriptionParser Errors<br />
** ''<executable> must be the outer most tag''<br />
** ''<executable> was found inside another tag''<br />
** ''<parameters> can only be inside <executable>''<br />
** ''<xxx> can only be used inside <parameters>''<br />
** ''<flag> can only contain one character''<br />
** ''<longname> can only contain letters, numbers and underscores and must start with an _ or letter''<br />
** ''<name> can only contain letters, numbers and underscores and must start with an _ or letter''<br />
<br />
* ModuleDescriptionParser Warnings<br />
** ''<xxx> is an unknown parameter tag'' : Probably a misspelled parameter type.<br />
<br />
* Compiler Errors<br />
** The generated C++ code may have syntax errors if invalid defaults are specified. These will show up during the C++ compilation.<br />
<br />
== Interfacing Legacy Executables ==<br />
<br />
GenerateCLP is only provided as a convenience. Users can use the same XML Module Description to interface C++, shell scripts, tcl programs and even Matlab.<br />
<br />
Example: [http://www.slicer.org/slicerWiki/index.php/Slicer3:FiberTrackingIntegration FiberTracking Integration]<br />
<br />
== Showing Progress in an Application ==<br />
<br />
Programs can communicate progress to the user in two ways. If the program is running an a stand-alone executable, it communicates with a simple XML syntax. If the program is loaded at run-time as a plugin library, it communicates through a C structure.<br />
<br />
The XML syntax is:<br />
<br />
<filter-start><br />
<filter-name><br />
''name of program section or algorithm''<br />
</filter-name><br />
<filter-comment><br />
''description of program section or algorithm''<br />
</filter-comment><br />
</filter-start><br />
<br />
<filter-progress><br />
''floating number from 0 to 1''<br />
</filter-progress><br />
<br />
<filter-end><br />
<filter-name><br />
''name of program section or algorithm''<br />
</filter-name><br />
<filter-time><br />
''execution time''<br />
</filter-time><br />
</filter-end><br />
<br />
The C structure that library plugins use is:<br />
<br />
extern "C" {<br />
struct ModuleProcessInformation<br />
{<br />
/** Inputs from calling application to the module **/<br />
unsigned char Abort;<br />
/** Outputs from the module to the calling application **/<br />
float Progress;<br />
char ProgressMessage[1024];<br />
void (*ProgressCallbackFunction)(void *);<br />
void *ProgressCallbackClientData;<br />
double ElapsedTime;<br />
}<br />
}<br />
<br />
Details on how to use this mechanism are illustrated in [https://github.com/Slicer/Slicer/blob/master/Base/CLI/itkPluginFilterWatcher.h itkPluginFilterWatcher.h].<br />
<br />
For vtk and itk execution model programs, two classes are available that make it simple to add progress. The classes, ''vtkPluginFilterWatcher'' and ''itk::PluginFilterWatcher'' use the vtk and itk command/observer mechanism to report progress.<br />
<br />
vtkPluginFilterWatcher (vtkAlgorithm *'''filter''', const char* '''comment''', ModuleProcessInformation *'''inf''', double '''fraction''', double '''start''') <br /> itk::PluginFilterWatcher (itk::ProcessObject '''filter''', const char* '''comment''', ModuleProcessInformation *'''inf''', double '''fraction''', double '''start''')<br />
<br />
: where:<br />
;; filter <br />
:: is the vtkAlgorithm or itk::ProcessObject to be watched.<br />
;; comment <br />
:: is a string that describes the algorithm.<br />
;; inf <br />
:: is an optional pointer to a structure that is used to communicate with the invoking program when the called program is used as a library plugin. If the pointer is 0, this prgram will not report progress if it is run as a library plugin. Default is 0.<br />
;; fraction <br />
:: is the fraction (0-1) of progress that will be reported by this watcher. This is used when multiple filters are run and each filter represents a proportion of the total progress. Default is 1.<br />
;; start <br />
:: is the offset (0-1) of the progress for this filter. This is added to the progress of the filter. The reported progress of the watched filter is ''start + fraction * filter_progress''.<br />
<br />
<br /> The following example produces progress for a simple vtk program. The variable CLPProcessInformation is automatically declared and set in the program's ''program''CLP.h file.<br />
<br />
<nowiki>#include "vtkPluginFilterWatcher.h"<br />
...<br />
vtkMarchingCubes *cubes = vtkMarchingCubes::New();<br />
cubes->SetInput(reader->GetOutput());<br />
vtkPluginFilterWatcher watchCubes(cubes, "Generate Isosurface", CLPProcessInformation, .5, 0.0);<br />
vtkDecimatePro *decimate = vtkDecimatePro::New();<br />
decimate->SetInput(cubes->GetOutput());<br />
vtkPluginFilterWatcher watchDecimate(decimate, "Reduce Triangle Count", CLPProcessInformation, .5, 0.5);<br />
decimate->Update();<br />
</nowiki><br />
<br />
The following example produces progress for a simple itk program:<br />
<br />
<nowiki>#include "itkPluginFilterWatcher.h<br />
...<br />
typedef itk::MedianImageFilter<ImageType,ImageType> FilterType;<br />
FilterType::Pointer median = FilterType::New();<br />
itk::PluginFilterWatcher watchMedian(median, "Denoise Image", CLPProcessInformation);<br />
</nowiki><br />
<br />
== Adding Module Logos to Slicer ==<br />
Slicer plugins, both libraries and executables, can specify plugin-specific logos. These appear in Slicer when a module is selected. The logos are specified in a .h header file. The KWWidget utility, KWConvertImageToHeader, converts a .png file into a .h header file containing the encoded image and additional information such as width, height and pixel size. [[Documentation-3.5#Requirements_for_Modules|See here for additional information.]]<br />
<br />
For Slicer, execution model plugin logos are stored in Modules/CLI/Resources. The corresponding image in .png format should be stored in Modules/CLI/ImageData. Othere plugins, created outside the Slicer tree, should store the logo and image in a similar location.<br />
<br />
To add a logo to a plugin:<br />
* Create a png image of the logo. The height of the logo should not exceed 40 pixels.<br />
* Convert the logo to the KWWidget icon format as follows. '''NOTE:''' the prefix of the image and header file must be the same for a plugin logo.<br />
<pre><br />
cd Modules/CLI<br />
KWConvertImageToHeader --base64 --zlib Resources/ITKLogo.h ImageData/ITKLogo.png<br />
</pre><br />
* Add the logo to the SEMMacroBuildCLImacro in the CMakeLists.txt file for the plugin using LOGO_HEADER parameter.<br />
<br />
=== Runtime specification of filter types ===<br />
<br />
ITK filters are templated over the images they process. The following code snippet shows how an execution model program can select the image types for filters based on the input images.<br />
<br />
First, include the utilites for plugin's:<br />
<pre><br />
#include "itkPluginUtilities.h"<br />
</pre><br />
<br />
Then, turn your main program into a templated procedure called ''DoIt'':<br />
<pre><br />
template<class T> int DoIt( int argc, char * argv[], T )<br />
{<br />
PARSE_ARGS;<br />
<br />
typedef itk::Image< T, 3 > InputImageType;<br />
typedef itk::Image< T, 3 > OutputImageType;<br />
.<br />
.<br />
.<br />
}<br />
</pre><br />
Then, create a main program that gets the native component type from one of the input file. Here that input file is ''inputVolume'':<br />
<pre><br />
int main( int argc, char * argv[] )<br />
{<br />
<br />
PARSE_ARGS;<br />
<br />
itk::ImageIOBase::IOPixelType pixelType;<br />
itk::ImageIOBase::IOComponentType componentType;<br />
<br />
try<br />
{<br />
itk::GetImageType (inputVolume, pixelType, componentType);<br />
<br />
// This filter handles all types<br />
<br />
switch (componentType)<br />
{<br />
case itk::ImageIOBase::UCHAR:<br />
return DoIt( argc, argv, static_cast<unsigned char>(0));<br />
break;<br />
case itk::ImageIOBase::CHAR:<br />
return DoIt( argc, argv, static_cast<char>(0));<br />
break;<br />
case itk::ImageIOBase::USHORT:<br />
return DoIt( argc, argv, static_cast<unsigned short>(0));<br />
break;<br />
case itk::ImageIOBase::SHORT:<br />
return DoIt( argc, argv, static_cast<short>(0));<br />
break;<br />
case itk::ImageIOBase::UINT:<br />
return DoIt( argc, argv, static_cast<unsigned int>(0));<br />
break;<br />
case itk::ImageIOBase::INT:<br />
return DoIt( argc, argv, static_cast<int>(0));<br />
break;<br />
case itk::ImageIOBase::ULONG:<br />
return DoIt( argc, argv, static_cast<unsigned long>(0));<br />
break;<br />
case itk::ImageIOBase::LONG:<br />
return DoIt( argc, argv, static_cast<long>(0));<br />
break;<br />
case itk::ImageIOBase::FLOAT:<br />
return DoIt( argc, argv, static_cast<float>(0));<br />
break;<br />
case itk::ImageIOBase::DOUBLE:<br />
return DoIt( argc, argv, static_cast<double>(0));<br />
break;<br />
case itk::ImageIOBase::UNKNOWNCOMPONENTTYPE:<br />
default:<br />
std::cout << "unknown component type" << std::endl;<br />
break;<br />
}<br />
}<br />
catch( itk::ExceptionObject &excep)<br />
{<br />
std::cerr << argv[0] << ": exception caught !" << std::endl;<br />
std::cerr << excep << std::endl;<br />
return EXIT_FAILURE;<br />
}<br />
return EXIT_SUCCESS;<br />
}<br />
<br />
</pre><br />
<br />
== Behind the Scenes ==<br />
<br />
A primary goal of the execution model is to relieve developers from developing GUI code and command line parsing code. This section describes the major components of the execution model implementation.<br />
<br />
=== Command Line Processing ===<br />
<br />
Command line processing parses command line arguments and populates internal program variables. Every Unix (and windows) program can receive an argument list through its main entry point. All C and C++ programmers are familiar with the ''int main (int argc, char *[] argv)'' entry point in their programs. Most computer languages including scripting languages provide a similar mechanism to retrieve command line arguments. Simple command line processing directly accesses the strings defined in argv.<br />
<br />
This snippet shows simple commmand line processing:<br />
<br />
int main (int argc, char *argv[])<br />
{<br />
if (argc < 2)<br />
{<br />
std::cout << "Usage: " << argv[0] << " filename" << std::endl;<br />
return -1;<br />
}<br />
std::cout << "The File is " << argv[1] << std::endl;<br />
return 0;<br />
}<br />
<br />
The simple approach works great for a small number of arguments. But larger numbers of arguments of varying types quickly make the processing code more complex and subject to error, both in coding and usage.<br />
<br />
int main (int argc, char *argv[])<br />
{<br />
if (argc < 5)<br />
{<br />
std::cout << "Usage: " << argv[0] << " iterations epsilon inputfile outputfile " << std::endl;<br />
return -1;<br />
}<br />
std::string inputfile(argv[3]);<br />
std::string outputfile(argv[4]);<br />
unsigned int iterations = atoi(argv[1]);<br />
float epsilon = atof(argv[2]);<br />
...<br />
return 0;<br />
}<br />
<br />
Adding flags (or options) to the command line makes the program easier to use but places a larger burden on the program developer. Each developer must ''invent'' a command line argument syntax and implement code to parse the command line. Even a simple example of this is too long to include in this description. This code snippet looks for just two command line arguments.<br />
<br />
int main (int argc, char *argv[])<br />
{<br />
if (argc < 3)<br />
{<br />
std::cout << "Usage: " << argv[0] << " [-i iterations] [-e epsilon] inputfile outputfile " << std::endl;<br />
return -1;<br />
}<br />
std::string inputfile;<br />
std::string outputfile;<br />
unsigned int iterations = 10; /* a default */<br />
float epsilon = .001; /* a defualt */<br />
++argc; /* skip program name */<br />
while (argc > 0)<br />
{<br />
if (strcmp(argv[argc], "-i")<br />
{<br />
iterations = atoi(argv[argc+1]);<br />
argc+=2;<br />
continue;<br />
<br />
else if (strcmp(argv[argc], "-e")<br />
{<br />
epsilon = atof(argv[argc+1]);<br />
argc+=2;<br />
continue;<br />
...<br />
}<br />
<br />
The code gets longer and longer as more options are added and must be rewritten every time a new programs is open.<br />
<br />
To solve this complexity issue, people have developed command line argument libraries. There are dozens, if not hundreds, of command line processing tools. For Slicer3 we looked at argument processors in vxl, nrrd, meta, kwsys and tclap. Each has its strengths and weaknesses. We chose [http://tclap.sourceforge.net/ The Templatized C++ Command Line Parser Library], '''TCLAP'''. '''TCLAP''' is implemented in ''include'' files and does not require a separate library build. As you will see later, the particular command line processing tool is, for the most part, transparent to the Slicer3 developer or user.<br />
<br />
But even these libraries require some work to use.<br />
<br />
==== TCLAP ====<br />
<br />
This example uses '''TCLAP''' to process a command line with 10 possible entries:<br />
<br />
int main ( int argc, char* argv[] ) {<br />
//<br />
// Define default values<br />
int HistogramBins = 30;<br />
int RandomSeed = 1234567;<br />
int SpatialSamples = 10000;<br />
float TranslationScale = 100.0;<br />
int Iterations = 200;<br />
int SplineOrder = 3;<br />
double MinimumStepSize = 0.00001;<br />
double MaximumStepSize = 0.005;<br />
bool PrintTransform = false;<br />
string fixedImageFileName;<br />
string movingImageFileName;<br />
string resampledImageFileName;<br />
//<br />
// Setup command line parsing<br />
try<br />
{<br />
TCLAP::CmdLine cl ( "Register2d", ' ', "$Revision: 1.1 $" );<br />
TCLAP::ValueArg<int> HistogramBinsArg ( "b", "histogrambins", "Number of histogram bins", false, 30, "integer", cl );<br />
TCLAP::ValueArg<int> IterationsArg ( "i", "iterations", "Number of Iterations", false, Iterations, "int", cl );<br />
TCLAP::ValueArg<double> MinimumStepSizeArg ( "m", "minstepsize", "Minimum Step Size", false, MinimumStepSize, "double", cl );<br />
TCLAP::ValueArg<double> MaximumStepSizeArg ( "x", "maxstepsize", "Maximum Step Size", false, MaximumStepSize, "double", cl );<br />
TCLAP::ValueArg<int> RandomSeedArg ( "r", "randomseed", "Random Seed", false, RandomSeed, "int", cl );<br />
TCLAP::ValueArg<int> SpatialSamplesArg ( "s", "spatialsamples", "Number of spatial samples", false, SpatialSamples, "int", cl );<br />
TCLAP::ValueArg<int> SplineOrderArg ( "o", "splineorder", "Order of spline for registration", false, SplineOrder, "int", cl );<br />
TCLAP::SwitchArg PrintTransformArg ( "p", "printtransform", "Print the final transform", PrintTransform, cl );<br />
TCLAP::ValueArg<float> TranslationScaleArg ( "t", "translationscale", "Translation scale", false, TranslationScale, "float", cl );<br />
TCLAP::UnlabeledValueArg<string> FixedImageArg ( "fixed", "Fixed image filename", "", "string", cl );<br />
TCLAP::UnlabeledValueArg<string> MovingImageArg ( "moving", "Moving image filename", "", "string", cl );<br />
TCLAP::UnlabeledValueArg<string> ResampledImageArg ( "resampled", "Resampled image filename", "", "string", cl );<br />
//<br />
// Parse the command line<br />
cl.parse ( argc, argv );<br />
//<br />
// Access the variables<br />
HistogramBins = HistogramBinsArg.getValue();<br />
Iterations = IterationsArg.getValue();<br />
MinimumStepSize = MinimumStepSizeArg.getValue();<br />
MaximumStepSize = MaximumStepSizeArg.getValue();<br />
RandomSeed = RandomSeedArg.getValue();<br />
SpatialSamples = SpatialSamplesArg.getValue();<br />
TranslationScale = TranslationScaleArg.getValue();<br />
PrintTransform = PrintTransformArg.getValue();<br />
fixedImageFileName = FixedImageArg.getValue();<br />
movingImageFileName = MovingImageArg.getValue();<br />
resampledImageFileName = ResampledImageArg.getValue();<br />
}<br />
catch ( ArgException e )<br />
{<br />
cerr << "error: " << e.error() << " for arg " << e.argId() << endl;<br />
exit ( EXIT_FAILURE );<br />
}<br />
<br />
You do get a lot for your investment here. Good error handling and help.<br />
<br />
=== Module Description Parser ===<br />
The XML parsing is done by the [https://github.com/Slicer/SlicerExecutionModel/tree/master/ModuleDescriptionParser ModuleDescriptionParser] class library. ''GenerateCLP'' and Slicer3 use this class library to parse the module XML descriptions. The class ''ModuleDescriptionParser'' has one method, '''Parse''', that converts the XML description into an object model. The resulting object model has one ''ModuleDescription'', one or more ''ModuleParameterGroup'' each of which has one or more ''ModuleParameter''. Each instance has access methods to retrieve information from the XML.<br />
* '''ModuleDescriptionParser''' - parser for command line module XML description.<br />
*: ''Parse(std::string xml, ModuleDescription module)'' - parse an xml string and populate a ModuleDescription.<br />
* '''ModuleDescription''' - contains information about a module <br />
*: const std::string ''GetCategory()'' : returns the contents of '''<category>'''.<br />
*: const std::string ''GetTitle()'' : returns the contents of '''<title>'''.<br />
*: const std::string ''GetDescription()'' : returns the contents of '''<description>'''.<br />
*: const std::string ''GetVersion()'' : returns the contents of '''<version>'''.<br />
*: const std::string ''GetDocumentationURL()'' : returns the contents of '''<documentationURL>'''.<br />
*: const std::string ''GetLicense()'' : returns the contents of '''<license>'''.<br />
*: const std::string ''GetContributor()'' : returns the contents of '''<contributor>'''.<br />
*: const std::vector<ModuleParameterGroup>& ''GetParameterGroups()'' : returns a vector of parameter groups.<br />
* '''ModuleParameterGroup''' - contains ModuleParameters for each parameter group.<br />
*: const std::string ''GetLabel'' - returns the contents of '''<label>'''.<br />
*: const std::string ''GetDescription()'' - returns the contents of the parameter group's '''<description>'''.<br />
*: const std::string ''GetAdvanced()'' - returns advanced attribute. Either "true" of "false".<br />
* '''ModuleParameter''' - contains information for a parameter.<br />
*:GetTag() - returns the parameter's tag, e.g. '''<integer>, <image>''', etc.<br />
*:GetName() - returns the parameter's '''<name>'''.<br />
*:GetLongFlag() - returns the parameter's '''<longflag>'''.<br />
*:GetLabel() - returns the parameter's '''<label>'''.<br />
*:GetMaximum() - returns the parameter's '''<maximum>''' constraint.<br />
*:GetMinimum() - returns the parameter's '''<minimum>''' constraint.<br />
*:GetStep() - returns the parameter's '''<step>'''.<br />
*:GetDescription() - returns the parameter's '''<description>'''.<br />
*:GetChannel() - returns the parameter's '''<channel>'''.<br />
*:GetIndex() - returns the parameter's '''<index>'''.<br />
*:GetDefault() - returns the parameter's '''<default>'''.<br />
*:GetFlag() - returns the parameter's '''<flag>'''.<br />
*:GetMultiple() - returns the parameter's multiple attribute, either "true" or "false".<br />
*:GetCoordinateSystem() - returns the parameter's coordinate system attribute, one of "lps", "ras", or "ijk".<br />
<br />
= Adding a new parameter type =<br />
<br />
Adding a ''new parameter type'' to the execution model involves several modifications:<br />
<br />
# A new XML tag needs to be defined to represent the new parameter type.<br />
# ModuleDescriptionParser needs to be modified to parse the new parameter tag type and specify how the command line processing code generation is going to represent the parameter type<br />
# CommandLineModuleGUI needs to be modified to construct the appropriate GUI element for the parameter type<br />
# CommandLineModuleLogic needs to be modified to put the parameter type onto the command line and request outputs parameter types be loaded back into Slicer and the MRML tree.<br />
# SlicerApplicationLogic needs to be modified to ingest any output parameter types back into Slicer and the MRML tree.<br />
# Additional modification are needed if the parameter is to be passed directly from the MRML tree without using the filesystem.<br />
# Updating the documentation.<br />
<br />
Simple parameter types can be passed directly on the command line. For instance, scalars, small lists, positions, etc. Complicated parameter types are passed to the module via '''abstract files'''. In some cases, these parameters are actually passed as files, where Slicer reads/write the data to the filesystem. In other cases, the parameters are passed as '''abstract files''' which are really references to within the Slicer memory model but appear to the Command Line Module as being a file. The Command Line Module is written using standard ITK ImageFileReader/ImageFileWriter classes but the ITK ImageIO factory mechanism is used to direct the ImageFileReader/ImageFileWriter to talk directly to the Slicer MRML tree instead of to the filesystem.<br />
<br />
== XML tag ==<br />
<br />
This is by far the easiest of the tasks involved in adding a new parameter type but it should not approached hastily. The XML description of a module is designed to be application agnostic. As such, parameter types should be described abstractly or generically. For instance, '''<geometry>''' tag corresponds to the Slicer model node, the '''<point>''' tag corresponds to the Slicer fiducial node, etc.<br />
<br />
Once the tag name is defined, you need to decide whether the parameter type could or should support the attributes '''multiple''', '''coordinateSystem''', or '''fileExtensions''' or perhaps a new attribute type.<br />
<br />
== Modifying ModuleDescriptionParser ==<br />
<br />
SlicerExecutionModel/ModuleDescriptionParser/ModuleDescriptionParser.cxx contains the code to parse the XML description of a module and represent that module description in C++ data structures. To add a new parameter type, this code needs to be modified.<br />
<br />
Two routines need to be modified in ModuleDescriptionParser, ''startElement()'' and ''endElement()''. For ''startElement()'', a new case block needs to be added for the parameter type. You can start by copying the case block for a similar parameter type. This case block is responsible for processing all the attributes for the tag and managing and reporting any errors. The case block may define the '''CPPType''', the ''ArgType''', and the '''StringToType''' needed for the code generation of the command line parsing. The '''CPPType''' is used by in the generated command line processing code to represent the parameter. This may be a simple C++ type or an STL container. The '''ArgType''' is the canonical type of each component of the parameter. The '''StringToType''' is the name of the method to use to convert the ASCII command line parameter to the final '''ArgType'''. The ''endElement()'' method merely needs a new case block to add the parameter to the description.<br />
<br />
ModuleDescriptionParser is fairly general, handling scalars, lists of scalars, and file types as parameter types. A parameter which does not fit these models will require considerable alterations to the ModuleDescriptionParser as well as the GenerateCLP.<br />
<br />
== Constructing a GUI for a new parameter type ==<br />
<br />
To have a GUI element appear in the module GUI for a new parameter type, Base\QTCLI\qSlicerCLIModuleUIHelper.cxx and Base\QTCLI\vtkSlicerCLIModuleLogic.cxx have to be modified. A new case block needs to be added to the '''BuildGUI()''' method for the new parameter type. This case block is triggered off the XML tag for the parameter type. The case block is responsible for the constructing the appropriate GUI element for the parameter, complete with specifying the label and help text. The design is very simple. A single widget is used for each parameter. If a more complicated GUI is needed with multiple widgets, then perhaps a new widget is needed to encapsulated a set of widgets or a naming convention can be added to manage all the widgets associated with a parameter. The widgets for the parameters are stored in a map, indexed by the '''name''' of the parameter.<br />
<br />
The vtkCommandLineModuleGUI (and vtkCommandLineModuleLogic) are designed to operate very generically with sets of parameters. The aforementioned map of widgets indexed by parameter '''name''' is one such example. The implementation of several of the methods in the vtkCommandLineModuleGUI (and vtkCommandLineModuleLogic) generically iterate over the widget map or over the parameter list. It is important to keep this in mind as new parameters are added. The design goal is to minimize the number of ''special cases'' in the code.<br />
<br />
Note that there may be separate case blocks for '''input''' and '''output''' parameter types.<br />
<br />
== Communicating the new parameter to the Command Line Module ==<br />
<br />
To communicate the new parameter type to and from a Command Line Module, the '''ApplyTask()''' method Slicer3/Modules/CommandLineModule/vtkCommandLineModuleLogic.cxx needs to be modified. <br />
<br />
If the parameter type is communicated to the command line module as a file (as opposed to directly on the command line as a number or srting), then there are several blocks of code to construct a temporary file name, keep track of whether that node needs to be written to the filesystem before execution, read from the filesystem after the execution, and deleted after execution completes. These blocks may need to be modified based on your new parameter type.<br />
<br />
The command line is constructed in two passes. First, a pass is made over the parameter list, building the portion of the command line for any parameters that have flags. Note that whether a parameter has a flag or not is up to the discretion of the module author and is not defined by the parameter type. Second, a pass is made to construction the portion of the command line for the parameters that do not have flags. These parameters are ordered appropriately, then placed on the command line. For parameters with flags, this code emits the flag and the parameter value. For the parameters without flags, this code emits just the parameter value. You will need to edit both of these passes to emit your parameter type. In most cases, this is simply a matter of grabbing the parameter value from the parameter and pushing it onto the command line. But some parameter types do require translation to a string appropriate for the command line.<br />
<br />
== Output parameters from the Command Line Modules ==<br />
<br />
Any outputs from a Command Line Module that are communicated via files are queued to be read by the main application thread. Command Line Modules run in a separate execution thread from the main GUI. This thread is not allowed to modify the Slicer GUI, so any results from the module that need to be read back into Slicer and displayed are queued for the main thread.<br />
<br />
The '''ProcessReadData()''' method of Slicer3/Base/Logic/vtkSlicerApplicationLogic.cxx pulls data from the queue to load back into Slicer and display. You may need to a case block for your new parameter type to construct the appropriate storage node and display node.<br />
<br />
== Communicating directly with the MRML tree ==<br />
<br />
Currently scalar image types can sent as parameters to shared object command line modules without going through the filesystem. Slicer provides a new ITK ImageIO factory that can communicated directly with the Slicer MRML tree. This ImageIO factory is in Slicer/Libs/MRMLIDImageIO. This approach can be extended for other image types such as vector or tensor volumes.<br />
<br />
For other constructs such as models and transforms, we'll need to see if an existing factory mechanism can be leverage to communicate directly with the Slicer MRML tree. An alternative may be to construct bridges specific to interfacing from a command line module to the Slicer MRML tree.<br />
<br />
== Adding new image types ==<br />
<br />
The Command Line Module support scalar, vector, tensor, and diffusion weighted images. Adding a new image type to the Command Line Module requires modify the sections of the code outlined above to manage GUI for the module, to construct temporary file names, to write image to disk, and load them back into the MRML tree. These blocks are easy to identify as case blocks on vtkMRMLScalarVolumeNode, vtkMRMLDiffusionTensorVolumeNode, etc. Note that the '''<image>''' tag supports a '''type''' attribute that can scalar, label, vector, tensor, or diffusion-weighted. The case block for '''image''' in the '''startElement()''' method of ModuleDescriptionParser would need to be extended to recognize a new type of image.<br />
<br />
=FAQ=<br />
<br />
==What facilities are available for third party software to interface with the NA-MIC Kit?==<br />
<br />
NA-MIC has committed to developing and supporting a generic batch processing interface. This is the Slicer3 Command Line Interface (CLI). The application programming interface (API) for CLI was worked out based on interactions with the community including input from those familiar with LONI, ITK, Python and Matlab software development practices.<br />
<br />
We believe this API already supports all the features required by to implement batch processing with BatchMake, the LONI Pipeline, and any other systems that we may want to work with in the future. The [[Slicer3:Execution_Model_Documentation|Slicer3 Execution Model]] provides a simple XML-based descriptor for command line arguments for input/output volumes and related parameters.<br />
<br />
==Which Third Party software has currently been interfaced to the NA-MIC Kit using the CLI?==<br />
<br />
*Batchmake (Kitware)<br />
*Python<br />
*Matlab<br />
<br />
== How do I debug my command line module? ==<br />
<br />
See [http://www.slicer.org/slicerWiki/index.php/Slicer3:Execution_Model_Documentation:Debugging Debugging]<br />
<br />
== How do I specify a Logo? ==<br />
<br />
You need to create a logo.h file (embedding the zipped pixel data as base64 strings in a C source code). There is a utility bundled with kwwidgets that accomplishes this. From a Slicer3 build directory you can do something like this:<br />
<br />
./Slicer3 --launch ../Slicer3-lib/KWWidgets-build/bin/KWConvertImageToHeader --base64 --zlib ~/Desktop/bioimagesuite_logo_www.h ~/Desktop/bioimagesuite_logo_www.png<br />
<br />
The resulting .h header file can then be used as an argument to the 'generateclp' cmake macro like this:<br />
<br />
generateclp(${CLP}_SOURCE ${CLP}.xml ${Slicer3_SOURCE_DIR}/Resources/bioimagesuite_logo_www.h)<br />
<br />
''Note: logos are limited in size: if your .h file contains a symbol like 'image_bioimagesuite_logo_www' it will work. But if it has several symbols like 'image_bioimagesuite_logo_www_1' 'image_bioimagesuite_logo_www_2' etc then the logo was too large and needs to be downsampled for use.''<br />
<br />
== What do I do if I want to use the CLI outside of slicer? ==<br />
<br />
In general CLI executables will depend on shared libraries provided by Slicer (such as ITKCommon, etc). To be sure your run-time environment resolves to the correct versions, you should always use a form like:<br />
./Slicer3 --launch <CLI><br />
or, you can start a shell from which you can run multiple CLI modules. Something like:<br />
./Slicer3 --launch xterm &<br />
<br />
We do not suggest hard-coding the runtime paths into, for example, your .bashrc since this may interfere with other programs and can make it hard to use multiple versions of slicer on the same account.<br />
<br />
= Laundry List =<br />
<br />
Use this section to describe any features that are missing, not documented, or require additional work.<br />
<br />
* Comments from Hans Johnson, June 4, 2010:<br />
** Enumerated types can only have about 3 characters in them, otherwise they don’t render in the slicer wizard at all.<br />
** There is no way to unset an enumerated type, and the values are cached even after leaving the module. If the default enumerated type is to be blank, and you select it, then you must restart slicer in order to make the blank option available again.<br />
** There is no way to have conditional options. For example if one flag is used, then other flag can not be used, so it should be disabled.<br />
<br />
* Documenting your module:<br />
** a convenience script has been developed by Hans Johnson, which creates a wiki page describing the command line module by extracting the information from the xml description [[SEMToMediaWiki.py]]. TODO: extract the default values for the parameters to be included in the documentation</div>Dzenanzhttps://www.slicer.org/w/index.php?title=Documentation/Nightly/Modules/Markups&diff=43099Documentation/Nightly/Modules/Markups2015-10-28T17:01:51Z<p>Dzenanz: Adding pointfile</p>
<hr />
<div><noinclude>{{documentation/versioncheck}}</noinclude><br />
<!-- ---------------------------- --><br />
{{documentation/{{documentation/version}}/module-header}}<br />
<!-- ---------------------------- --><br />
<br />
<!-- ---------------------------- --><br />
{{documentation/{{documentation/version}}/module-section|Introduction and Acknowledgements}}<br />
{{documentation/{{documentation/version}}/module-introduction-start|{{documentation/modulename}}}}<br />
{{documentation/{{documentation/version}}/module-introduction-row}}<br />
This work is part of the National Alliance for Medical Image Computing (NA-MIC), funded by the National Institutes of Health through the NIH Roadmap for Medical Research, Grant U54 EB005149. Information on NA-MIC can be obtained from the [http://www.na-mic.org/ NA-MIC website].<br><br />
Author: Nicole Aucoin, SPL, BWH<br><br />
Contact: Nicole Aucoin, <email>nicole@bwh.harvard.edu</email><br><br />
{{documentation/{{documentation/version}}/module-introduction-row}}<br />
{{documentation/{{documentation/version}}/module-introduction-logo-gallery<br />
|{{collaborator|logo|spl}}|{{collaborator|longname|spl}}<br />
}}<br />
{{documentation/{{documentation/version}}/module-introduction-end}}<br />
<br />
<!-- ---------------------------- --><br />
{{documentation/{{documentation/version}}/module-section|Module Description}}<br />
This module is used to manage markups in Slicer. <br />
<br />
It is based on the functionality in the Slicer 3.6 Fiducials module, with elements from the Slicer 4.2 Annotations module. <br />
<br />
Currently, only fiducials are supported.<br />
<br />
{{documentation/{{documentation/version}}/module-description}}<br />
<br />
Development history can be found in the [[documentation/{{documentation/version}}/Modules/{{documentation/modulename}}#References|references]].<br />
<br />
<!-- ---------------------------- --><br />
{{documentation/{{documentation/version}}/module-section|Use Cases}}<br />
<br />
This module allows you to add new markups, edit markups in a list, delete markups and modify display properties of markups.<br />
<br />
Markups can be passed to command line modules (CLIs) using the <pre>--point</pre> or <pre>--pointfile</pre> argument. <br />
<br />
The Mouse mode tool bar can be used to place new markups by positioning the mouse and left clicking in the viewers. Once you are in Place mode and placing Markups fiducials, you can use the 'p' key to place a new fiducial while the Markups GUI is open. You can also use a right button click to stop placing fiducials when in persistent mode.<br />
<br />
You can place fiducials and use the GUI to jump slices or the 3D view to those locations. If you select fiducials in the table, you can right click to get the summed linear distance between their locations.<br />
<br />
The Markups module can convert Annotation fiducials into Markups fiducials, and will offer the user the option of doing it automatically when the Markups GUI is entered. There is also a manual button under the Advanced tab.<br />
<br />
<!-- ---------------------------- --><br />
{{documentation/{{documentation/version}}/module-section|Tutorials}}<br />
<br />
N/A<br />
<br />
<!-- ---------------------------- --><br />
<br />
== Panels ==<br />
<br />
<gallery><br />
File:Markups-GUI-Collapsed.png | GUI<br />
File:Markups-ModifyList.png | Select the active list, set visibility and lock flags, set uniform scale.<br />
File:Markups-JumpSlices-44.png | Jump the 2D slice view windows to the coordinates of the highlighted markup.<br />
File:Markups-ModifyMarkupsInList.png | Modify Markups in list: toggle visibility, selected, and lock flags, delete.<br />
File:Markups-Table.png | Table showing the fiducials in the active Markups list<br />
File:Markups-Advanced.png | Advanced panel: move markups up or down, add a markup, name format, convert annotation fiducials, display properties for the list of markups<br />
File:Markups-DisplayProperties.png | Display properties, inside the Advaned panel, includes 2d projection settings.<br />
File:Markups-MouseToolbar.png | Place Markup fiducials by setting the Mouse mode in the tool bar<br />
</gallery><br />
{{documentation/{{documentation/version}}/module-section|Panels and their use}}<br />
<br />
{{documentation/{{documentation/version}}/module-parametersdescription}}<br />
<br />
<!-- ---------------------------- --><br />
{{documentation/{{documentation/version}}/module-section|Similar Modules}}<br />
<br />
*[[Documentation/Nightly/Modules/Annotations | Annotations]] module is similar, and will be deprecated as the functionality is moved to the Markups module.<br />
*[https://github.com/Slicer/Slicer/blob/master/Modules/Scripted/Endoscopy/Endoscopy.py#L303 Endoscopy module] uses fiducials<br />
<br />
<!-- ---------------------------- --><br />
{{documentation/{{documentation/version}}/module-section|References}}<br />
<br />
*[http://wiki.na-mic.org/Wiki/index.php/2013_Summer_Project_Week:MarkupsModuleSummer2013 Summer 2013 Project Week project page]<br />
*[http://wiki.na-mic.org/Wiki/index.php/2013_Project_Week:MarkupsModule Winter 2013 Project Week project page]<br />
*[http://wiki.na-mic.org/Wiki/index.php/2012_Summer_Project_Week:AnnotationModule Summer 2012 Project Week project page]<br />
<br />
<!-- ---------------------------- --><br />
{{documentation/{{documentation/version}}/module-section|Information for Developers}}<br />
{{documentation/{{documentation/version}}/module-developerinfo}}<br />
<br />
== Python ==<br />
<br />
=== Open the Markups Module ===<br />
<br />
You can set the Markups module gui to be the current module gui using this command:<br />
<br />
slicer.util.mainWindow().moduleSelector().selectModule('Markups')<br />
<br />
=== Load From File ===<br />
<br />
Markups fiducials can be loaded from file:<br />
<br />
slicer.util.loadMarkupsFiducialList('/path/to/list/F.fcsv')<br />
<br />
=== Adding Fiducials Programatically ===<br />
<br />
Markups fiducials can be added to the currently active list from the python console by using the following module logic command:<br />
<br />
slicer.modules.markups.logic().AddFiducial()<br />
<br />
The command with no arguments will place a new fiducial at the origin. You can also pass it an initial location:<br />
<br />
slicer.modules.markups.logic().AddFiducial(1.0, -2.0, 3.3)<br />
<br />
=== Adding Fiducials via Mouse Clicks ===<br />
<br />
You can also set the mouse mode into Markups fiducial placement by calling:<br />
<br />
placeModePersistence = 1<br />
slicer.modules.markups.logic().StartPlaceMode(placeModePersistence)<br />
<br />
A lower level way to do this is via the selection and interaction nodes:<br />
selectionNode = slicer.mrmlScene.GetNodeByID("vtkMRMLSelectionNodeSingleton")<br />
selectionNode.SetReferenceActivePlaceNodeClassName("vtkMRMLMarkupsFiducialNode")<br />
interactionNode = slicer.mrmlScene.GetNodeByID("vtkMRMLInteractionNodeSingleton")<br />
placeModePersistence = 1<br />
interactionNode.SetPlaceModePersistence(placeModePersistence)<br />
# mode 1 is Place, can also be accessed via slicer.vtkMRMLInteractionNode().Place<br />
interactionNode.SetCurrentInteractionMode(1)<br />
<br />
To switch back to view transform once you're done placing fiducials:<br />
interactionNode = slicer.mrmlScene.GetNodeByID("vtkMRMLInteractionNodeSingleton")<br />
interactionNode.SwitchToViewTransformMode()<br />
# also turn off place mode persistence if required<br />
interactionNode.SetPlaceModePersistence(0)<br />
<br />
=== Access to Fiducial Properties ===<br />
<br />
Each vtkMRMLMarkupsFiducialNode has a vector of points in it which can be accessed from python:<br />
<br />
fidNode = getNode("vtkMRMLMarkupsFiducialNode1")<br />
n = fidNode.AddFiducial(4.0, 5.5, -6.0)<br />
fidNode.SetNthFiducialLabel(n, "new label")<br />
# each markup is given a unique id which can be accessed from the superclass level<br />
id1 = fidNode.GetNthMarkupID(n)<br />
# manually set the position<br />
fidNode.SetNthFiducialPosition(n, 6.0, 7.0, 8.0)<br />
# set the label<br />
fidNode.SetNthFiducialLabel(n, "New label")<br />
# set the selected flag, only selected = 1 fiducials will be passed to CLIs<br />
fidNode.SetNthFiducialSelected(n, 1)<br />
# set the visibility flag<br />
fidNode.SetNthFiducialVisibility(n, 0) <br />
<br />
You can loop over the fiducials in a list and get the coordinates:<br />
<br />
fidList = slicer.util.getNode('F')<br />
numFids = fidList.GetNumberOfFiducials()<br />
for i in range(numFids):<br />
ras = [0,0,0]<br />
fidList.GetNthFiducialPosition(i,ras)<br />
# the world position is the RAS position with any transform matrices applied<br />
world = [0,0,0,0]<br />
fidList.GetNthFiducialWorldCoordinates(0,world)<br />
print i,": RAS =",ras,", world =",world<br />
<br />
You can also look at the sample code in the [https://github.com/Slicer/Slicer/blob/master/Modules/Scripted/Endoscopy/Endoscopy.py#L287 Endoscopy module] to see how python is used to access fiducials from a scripted module.<br />
<br />
=== VTK Widget access ===<br />
<br />
The Markups 3D fiducial displayable manager can be accessed from python allowing debugging of the vtkSeedWidget:<br />
<br />
fidNode = slicer.mrmlScene.GetNodeByID("vtkMRMLMarkupsFiducialNode1")<br />
lm = slicer.app.layoutManager()<br />
td = lm.threeDWidget(0)<br />
ms = vtk.vtkCollection()<br />
td.getDisplayableManagers(ms)<br />
for i in range(ms.GetNumberOfItems()): <br />
m = ms.GetItemAsObject(i)<br />
if m.GetClassName() == "vtkMRMLMarkupsFiducialDisplayableManager3D":<br />
print i, m.GetClassName()<br />
h = m.GetHelper()<br />
seedWidget = h.GetWidget(fidNode)<br />
seedRepresentation = seedWidget.GetSeedRepresentation()<br />
handleRep2 = seedRepresentation.GetHandleRepresentation(2)<br />
print handleRep2.GetDisplayPosition()<br />
print handleRep2.GetWorldPosition()<br />
<br />
<br />
The Markups 2D fiducial displayable manager can be accessed from python as well to debug the seed widget in 2D. For the Red slice viewer:<br />
<br />
fidNode = slicer.mrmlScene.GetNodeByID("vtkMRMLMarkupsFiducialNode1")<br />
lm = slicer.app.layoutManager()<br />
redWidget = lm.sliceWidget("Red")<br />
redView = redWidget.sliceView()<br />
ms = vtk.vtkCollection()<br />
redView.getDisplayableManagers(ms)<br />
for i in range(ms.GetNumberOfItems()):<br />
m = ms.GetItemAsObject(i)<br />
if m.GetClassName() == "vtkMRMLMarkupsFiducialDisplayableManager2D":<br />
print i, m.GetClassName()<br />
h = m.GetHelper()<br />
seedWidget = h.GetWidget(fidNode)<br />
seedRepresentation = seedWidget.GetSeedRepresentation()<br />
handleRep = seedRepresentation.GetHandleRepresentation(0)<br />
print handleRep.GetDisplayPosition()<br />
<br />
== C++ ==<br />
<br />
===Selection and Interaction===<br />
<br />
For the selection and interaction nodes, make sure that you access the singleton nodes already in the scene (rather than making your own) via (caveat: this call works on displayable managers, you may have to go a different route to get at the application logic):<br />
vtkMRMLApplicationLogic *mrmlAppLogic = this->GetMRMLApplicationLogic();<br />
vtkMRMLInteractionNode *inode = mrmlAppLogic->GetInteractionNode();<br />
vtkMRMLSelectionNode *snode = mrmlAppLogic->GetSelectionNode();<br />
If you can't get at the mrml application logic, you can get the nodes from the scene:<br />
vtkMRMLInteractionNode *interactionNode = vtkMRMLInteractionNode::SafeDownCast(this->GetMRMLScene()->GetNodeByID("vtkMRMLInteractionNodeSingleton"));<br />
<br />
You can then call methods on the nodes or add your own event observers as in<br />
vtkSlicerMarkupsModuleLogic::ObserveMRMLScene<br />
You can see vtkSlicerMarkupsModuleLogic::ProcessMRMLNodesEvents to see how to respond to interaction node changes.<br />
<br />
Slicer4/Base/QTGUI/qSlicerMouseModeToolBar.cxx has a lot of the code you'll need as well, with a slightly different way of getting at the mrml app logic to access the nodes.<br />
<br />
The calls you need to make to switch into placing fiducials with the mouse are:<br />
selectionNode->SetReferenceActivePlaceNodeClassName("vtkMRMLMarkupsFiducialNode");<br />
interactionNode->SetCurrentInteractionMode(vtkMRMLInteractionNode::Place);<br />
If you don't set PlaceModePersistence on the interaction node to 1, the mouse mode/current interaction mode will automatically go back to view transform after one fiducial has been placed, and you just need to reset the current interaction mode for future placing (the active place node class name and ID are persistent).<br />
<br />
=== Programmatic Access to Fiducials ===<br />
<br />
This section explains how to access fiducial nodes added into the scene by the user.<br />
<br />
In your module's Logic class, overwrite SetMRMLSceneInternal to observe the NodeAddedEvent of the scene:<br />
void vtkSlicerMYMODULEModuleLogic::SetMRMLSceneInternal(vtkMRMLScene * newScene)<br />
{<br />
vtkNew<vtkIntArray> events;<br />
events->InsertNextValue(vtkMRMLScene::NodeAddedEvent);<br />
// Optionally you can add more events here, <br />
// such as vtkMRMLScene::NodeRemovedEvent<br />
this->SetAndObserveMRMLSceneEventsInternal(newScene, events.GetPointer());<br />
}<br />
Via the set and observe mrml scene macro, anytime a node is added into the scene, it will trigger the base Logic class method<br />
[http://slicer.org/doc/html/classvtkMRMLAbstractLogic.html#a51fdf00ecfc18c1a1465d4bccb789ef1 vtkMRMLAbstractLogic::ProcessMRMLNodesEvents] which calls the virtual method <code>vtkMRMLAbstractLogic::OnMRMLSceneNodeAdded(vtkMRMLNode* node)</code><br />
Reimplement that method in your logic class:<br />
void vtkSlicerMYMODULEModuleLogic::OnMRMLSceneNodeAdded(vtkMRMLNode* addedNode)<br />
{<br />
vtkMRMLMarkupsFiducialNode* fidNode =<br />
vtkMRMLMarkupsFiducialNode::SafeDownCast(addedNode);<br />
if (fidNode)<br />
{<br />
// here you write what you need to do when a <br />
// fiducial node is added into the scene<br />
}<br />
}<br />
<br />
=== File Format===<br />
<br />
The Markups Fiducial storage node uses a comma separated value file to store the fiducials on disk. The format is:<br />
<br />
A leading comment line giving the Slicer version number:<br />
# Markups fiducial file version = 4.3<br />
A comment line giving the coordinate system, RAS = 0, LPS = 1, IJK = 2 (IJK is currently not supported, RAS is printed instead)<br />
# CoordinateSystem = 0<br />
A comment line explaining the fields in the csv<br />
# columns = id,x,y,z,ow,ox,oy,oz,vis,sel,lock,label,desc,associatedNodeID<br />
Then comes the fiducials, one per line, for example:<br />
vtkMRMLMarkupsFiducialNode_0,1.29,-40.18,60.15,0,0,0,1,1,1,0,F-1,,<br />
*id = a string giving a unique id for this fiducial, usually based on the class name<br />
*x,y,z = the floating point coordinate of the fiducial point<br />
*ow,ox,oy,oz = the orientation quaternion of this fiducial, angle and axis, default 0,0,0,1 (or 0,0,0,0,0,0,1.0)<br />
*vis = the visibility flag for this fiducial, 0 or 1, default 1<br />
*sel = the selected flag for this fiducial, 0 or 1, default 1<br />
*lock = the locked flag for this fiducial, 0 or 1, default 0<br />
*label = the name for this fiducial, displayed beside the glyph, with quotes around it if there is a comma in the field<br />
*desc = a string description for this fiducial, optional<br />
*associatedNodeID = an id of a node in the scene with which the fiducial is associated, for example the volume or model on which the fiducial was placed, optional<br />
<br />
The file can be parsed using stringstream and getline:<br />
<br />
std::fstream fstr;<br />
fstr.open(fileName.c_str(), std::fstream::in);<br />
char line[1024];<br />
while (fstr.good())<br />
{<br />
fstr.getline(line, 1024);<br />
std::stringstream ss(line);<br />
std::string component;<br />
getline(ss, component, ',');<br />
[...]<br />
}<br />
<br />
<!-- ---------------------------- --><br />
{{documentation/{{documentation/version}}/module-footer}}<br />
<!-- ---------------------------- --></div>Dzenanzhttps://www.slicer.org/w/index.php?title=Documentation/4.4/Modules/Markups&diff=43098Documentation/4.4/Modules/Markups2015-10-28T16:59:03Z<p>Dzenanz: Undo revision 43097 by Dzenanz (Talk)</p>
<hr />
<div><noinclude>{{documentation/versioncheck}}</noinclude><br />
<!-- ---------------------------- --><br />
{{documentation/{{documentation/version}}/module-header}}<br />
<!-- ---------------------------- --><br />
<br />
<!-- ---------------------------- --><br />
{{documentation/{{documentation/version}}/module-section|Introduction and Acknowledgements}}<br />
{{documentation/{{documentation/version}}/module-introduction-start|{{documentation/modulename}}}}<br />
{{documentation/{{documentation/version}}/module-introduction-row}}<br />
This work is part of the National Alliance for Medical Image Computing (NA-MIC), funded by the National Institutes of Health through the NIH Roadmap for Medical Research, Grant U54 EB005149. Information on NA-MIC can be obtained from the [http://www.na-mic.org/ NA-MIC website].<br><br />
Author: Nicole Aucoin, SPL, BWH<br><br />
Contact: Nicole Aucoin, <email>nicole@bwh.harvard.edu</email><br><br />
{{documentation/{{documentation/version}}/module-introduction-row}}<br />
{{documentation/{{documentation/version}}/module-introduction-logo-gallery<br />
|{{collaborator|logo|spl}}|{{collaborator|longname|spl}}<br />
}}<br />
{{documentation/{{documentation/version}}/module-introduction-end}}<br />
<br />
<!-- ---------------------------- --><br />
{{documentation/{{documentation/version}}/module-section|Module Description}}<br />
This module is used to manage markups in Slicer. <br />
<br />
It is based on the functionality in the Slicer 3.6 Fiducials module, with elements from the Slicer 4.2 Annotations module. <br />
<br />
Currently, only fiducials are supported.<br />
<br />
{{documentation/{{documentation/version}}/module-description}}<br />
<br />
Development history can be found in the [[documentation/{{documentation/version}}/Modules/{{documentation/modulename}}#References|references]].<br />
<br />
<!-- ---------------------------- --><br />
{{documentation/{{documentation/version}}/module-section|Use Cases}}<br />
<br />
This module allows you to add new markups, edit markups in a list, delete markups and modify display properties of markups.<br />
<br />
Markups can be passed to command line modules (CLIs) using the <pre>--point</pre> argument. <br />
<br />
The Mouse mode tool bar can be used to place new markups by positioning the mouse and left clicking in the viewers. Once you are in Place mode and placing Markups fiducials, you can use the 'p' key to place a new fiducial while the Markups GUI is open. You can also use a right button click to stop placing fiducials when in persistent mode.<br />
<br />
The Markups module can convert Annotation fiducials into Markups fiducials, and will offer the user the option of doing it automatically when the Markups GUI is entered. There is also a manual button under the Advanced tab.<br />
<br />
<!-- ---------------------------- --><br />
{{documentation/{{documentation/version}}/module-section|Tutorials}}<br />
<br />
N/A<br />
<br />
<!-- ---------------------------- --><br />
<br />
== Panels ==<br />
<br />
<gallery><br />
File:Markups-GUI-Collapsed.png | GUI<br />
File:Markups-ModifyList.png | Select the active list, set visibility and lock flags, set uniform scale.<br />
File:Markups-JumpSlices-44.png | Jump the 2D slice view windows to the coordinates of the highlighted markup.<br />
File:Markups-ModifyMarkupsInList.png | Modify Markups in list: toggle visibility, selected, and lock flags, delete.<br />
File:Markups-Table.png | Table showing the fiducials in the active Markups list<br />
File:Markups-Advanced.png | Advanced panel: move markups up or down, add a markup, name format, convert annotation fiducials, display properties for the list of markups<br />
File:Markups-DisplayProperties.png | Display properties, inside the Advaned panel, includes 2d projection settings.<br />
File:Markups-MouseToolbar.png | Place Markup fiducials by setting the Mouse mode in the tool bar<br />
</gallery><br />
{{documentation/{{documentation/version}}/module-section|Panels and their use}}<br />
<br />
{{documentation/{{documentation/version}}/module-parametersdescription}}<br />
<br />
<!-- ---------------------------- --><br />
{{documentation/{{documentation/version}}/module-section|Similar Modules}}<br />
<br />
*[[Documentation/Nightly/Modules/Annotations | Annotations]] module is similar, and will be deprecated as the functionality is moved to the Markups module.<br />
*[https://github.com/Slicer/Slicer/blob/master/Modules/Scripted/Endoscopy/Endoscopy.py#L303 Endoscopy module] uses fiducials<br />
<br />
<!-- ---------------------------- --><br />
{{documentation/{{documentation/version}}/module-section|References}}<br />
<br />
*[http://wiki.na-mic.org/Wiki/index.php/2013_Summer_Project_Week:MarkupsModuleSummer2013 Summer 2013 Project Week project page]<br />
*[http://wiki.na-mic.org/Wiki/index.php/2013_Project_Week:MarkupsModule Winter 2013 Project Week project page]<br />
*[http://wiki.na-mic.org/Wiki/index.php/2012_Summer_Project_Week:AnnotationModule Summer 2012 Project Week project page]<br />
<br />
<!-- ---------------------------- --><br />
{{documentation/{{documentation/version}}/module-section|Information for Developers}}<br />
{{documentation/{{documentation/version}}/module-developerinfo}}<br />
<br />
== Python ==<br />
<br />
=== Load From File ===<br />
<br />
Markups fiducials can be loaded from file:<br />
<br />
slicer.util.loadMarkupsFiducialList('/path/to/list/F.fcsv')<br />
<br />
=== Adding Fiducials Programatically ===<br />
<br />
Markups fiducials can be added to the currently active list from the python console by using the following module logic command:<br />
<br />
slicer.modules.markups.logic().AddFiducial()<br />
<br />
The command with no arguments will place a new fiducial at the origin. You can also pass it an initial location:<br />
<br />
slicer.modules.markups.logic().AddFiducial(1.0, -2.0, 3.3)<br />
<br />
=== Adding Fiducials via Mouse Clicks ===<br />
<br />
You can also set the mouse mode into Markups fiducial placement by calling:<br />
<br />
placeModePersistence = 1<br />
slicer.modules.markups.logic().StartPlaceMode(placeModePersistence)<br />
<br />
A lower level way to do this is via the selection and interaction nodes:<br />
selectionNode = slicer.mrmlScene.GetNodeByID("vtkMRMLSelectionNodeSingleton")<br />
selectionNode.SetReferenceActivePlaceNodeClassName("vtkMRMLMarkupsFiducialNode")<br />
interactionNode = slicer.mrmlScene.GetNodeByID("vtkMRMLInteractionNodeSingleton")<br />
placeModePersistence = 1<br />
interactionNode.SetPlaceModePersistence(placeModePersistence)<br />
# mode 1 is Place, can also be accessed via slicer.vtkMRMLInteractionNode().Place<br />
interactionNode.SetCurrentInteractionMode(1)<br />
<br />
=== Access to Fiducial Properties ===<br />
<br />
Each vtkMRMLMarkupsFiducialNode has a vector of points in it which can be accessed from python:<br />
<br />
fidNode = getNode("vtkMRMLMarkupsFiducialNode1")<br />
n = fidNode.AddFiducial(4.0, 5.5, -6.0)<br />
fidNode.SetNthFiducialLabel(n, "new label")<br />
# each markup is given a unique id which can be accessed from the superclass level<br />
id1 = fidNode.GetNthMarkupID(n)<br />
# manually set the position<br />
fidNode.SetNthFiducialPosition(n, 6.0, 7.0, 8.0)<br />
# set the label<br />
fidNode.SetNthFiducialLabel(n, "New label")<br />
# set the selected flag, only selected = 1 fiducials will be passed to CLIs<br />
fidNode.SetNthFiducialSelected(n, 1)<br />
# set the visibility flag<br />
fidNode.SetNthFiducialVisibility(n, 0) <br />
<br />
You can loop over the fiducials in a list and get the coordinates:<br />
<br />
fidList = slicer.util.getNode('F')<br />
numFids = fidList.GetNumberOfFiducials()<br />
for i in range(numFids):<br />
ras = [0,0,0]<br />
fidList.GetNthFiducialPosition(i,ras)<br />
# the world position is the RAS position with any transform matrices applied<br />
world = [0,0,0,0]<br />
fidList.GetNthFiducialWorldCoordinates(0,world)<br />
print i,": RAS =",ras,", world =",world<br />
<br />
You can also look at the sample code in the [https://github.com/Slicer/Slicer/blob/master/Modules/Scripted/Endoscopy/Endoscopy.py#L287 Endoscopy module] to see how python is used to access fiducials from a scripted module.<br />
<br />
=== VTK Widget access ===<br />
<br />
The Markups 3D fiducial displayable manager can be accessed from python allowing debugging of the vtkSeedWidget:<br />
<br />
fidNode = slicer.mrmlScene.GetNodeByID("vtkMRMLMarkupsFiducialNode1")<br />
lm = slicer.app.layoutManager()<br />
td = lm.threeDWidget(0)<br />
ms = vtk.vtkCollection()<br />
td.getDisplayableManagers(ms)<br />
for i in range(ms.GetNumberOfItems()): <br />
m = ms.GetItemAsObject(i)<br />
if m.GetClassName() == "vtkMRMLMarkupsFiducialDisplayableManager3D":<br />
print i, m.GetClassName()<br />
h = m.GetHelper()<br />
seedWidget = h.GetWidget(fidNode)<br />
seedRepresentation = seedWidget.GetSeedRepresentation()<br />
handleRep2 = seedRepresentation.GetHandleRepresentation(2)<br />
print handleRep2.GetDisplayPosition()<br />
print handleRep2.GetWorldPosition()<br />
<br />
<br />
The Markups 2D fiducial displayable manager can be accessed from python as well to debug the seed widget in 2D. For the Red slice viewer:<br />
<br />
fidNode = slicer.mrmlScene.GetNodeByID("vtkMRMLMarkupsFiducialNode1")<br />
lm = slicer.app.layoutManager()<br />
redWidget = lm.sliceWidget("Red")<br />
redView = redWidget.sliceView()<br />
ms = vtk.vtkCollection()<br />
redView.getDisplayableManagers(ms)<br />
for i in range(ms.GetNumberOfItems()):<br />
m = ms.GetItemAsObject(i)<br />
if m.GetClassName() == "vtkMRMLMarkupsFiducialDisplayableManager2D":<br />
print i, m.GetClassName()<br />
h = m.GetHelper()<br />
seedWidget = h.GetWidget(fidNode)<br />
seedRepresentation = seedWidget.GetSeedRepresentation()<br />
handleRep = seedRepresentation.GetHandleRepresentation(0)<br />
print handleRep.GetDisplayPosition()<br />
<br />
== C++ ==<br />
<br />
===Selection and Interaction===<br />
<br />
For the selection and interaction nodes, make sure that you access the singleton nodes already in the scene (rather than making your own) via (caveat: this call works on displayable managers, you may have to go a different route to get at the application logic):<br />
vtkMRMLApplicationLogic *mrmlAppLogic = this->GetMRMLApplicationLogic();<br />
vtkMRMLInteractionNode *inode = mrmlAppLogic->GetInteractionNode();<br />
vtkMRMLSelectionNode *snode = mrmlAppLogic->GetSelectionNode();<br />
If you can't get at the mrml application logic, you can get the nodes from the scene:<br />
vtkMRMLInteractionNode *interactionNode = vtkMRMLInteractionNode::SafeDownCast(this->GetMRMLScene()->GetNodeByID("vtkMRMLInteractionNodeSingleton"));<br />
<br />
You can then call methods on the nodes or add your own event observers as in<br />
vtkSlicerMarkupsModuleLogic::ObserveMRMLScene<br />
You can see vtkSlicerMarkupsModuleLogic::ProcessMRMLNodesEvents to see how to respond to interaction node changes.<br />
<br />
Slicer4/Base/QTGUI/qSlicerMouseModeToolBar.cxx has a lot of the code you'll need as well, with a slightly different way of getting at the mrml app logic to access the nodes.<br />
<br />
The calls you need to make to switch into placing fiducials with the mouse are:<br />
selectionNode->SetReferenceActivePlaceNodeClassName("vtkMRMLMarkupsFiducialNode");<br />
interactionNode->SetCurrentInteractionMode(vtkMRMLInteractionNode::Place);<br />
If you don't set PlaceModePersistence on the interaction node to 1, the mouse mode/current interaction mode will automatically go back to view transform after one fiducial has been placed, and you just need to reset the current interaction mode for future placing (the active place node class name and ID are persistent).<br />
<br />
=== Programmatic Access to Fiducials ===<br />
<br />
This section explains how to access fiducial nodes added into the scene by the user.<br />
<br />
In your module's Logic class, overwrite SetMRMLSceneInternal to observe the NodeAddedEvent of the scene:<br />
void vtkSlicerMYMODULEModuleLogic::SetMRMLSceneInternal(vtkMRMLScene * newScene)<br />
{<br />
vtkNew<vtkIntArray> events;<br />
events->InsertNextValue(vtkMRMLScene::NodeAddedEvent);<br />
// Optionally you can add more events here, <br />
// such as vtkMRMLScene::NodeRemovedEvent<br />
this->SetAndObserveMRMLSceneEventsInternal(newScene, events.GetPointer());<br />
}<br />
Via the set and observe mrml scene macro, anytime a node is added into the scene, it will trigger the base Logic class method<br />
[http://slicer.org/doc/html/classvtkMRMLAbstractLogic.html#a51fdf00ecfc18c1a1465d4bccb789ef1 vtkMRMLAbstractLogic::ProcessMRMLNodesEvents] which calls the virtual method <code>vtkMRMLAbstractLogic::OnMRMLSceneNodeAdded(vtkMRMLNode* node)</code><br />
Reimplement that method in your logic class:<br />
void vtkSlicerMYMODULEModuleLogic::OnMRMLSceneNodeAdded(vtkMRMLNode* addedNode)<br />
{<br />
vtkMRMLMarkupsFiducialNode* fidNode =<br />
vtkMRMLMarkupsFiducialNode::SafeDownCast(addedNode);<br />
if (fidNode)<br />
{<br />
// here you write what you need to do when a <br />
// fiducial node is added into the scene<br />
}<br />
}<br />
<br />
=== File Format===<br />
<br />
The Markups Fiducial storage node uses a comma separated value file to store the fiducials on disk. The format is:<br />
<br />
A leading comment line giving the Slicer version number:<br />
# Markups fiducial file version = 4.3<br />
A comment line giving the coordinate system, RAS = 0, LPS = 1, IJK = 2 (IJK is currently not supported, RAS is printed instead)<br />
# CoordinateSystem = 0<br />
A comment line explaining the fields in the csv<br />
# columns = id,x,y,z,ow,ox,oy,oz,vis,sel,lock,label,desc,associatedNodeID<br />
Then comes the fiducials, one per line, for example:<br />
vtkMRMLMarkupsFiducialNode_0,1.29,-40.18,60.15,0,0,0,1,1,1,0,F-1,,<br />
*id = a string giving a unique id for this fiducial, usually based on the class name<br />
*x,y,z = the floating point coordinate of the fiducial point<br />
*ow,ox,oy,oz = the orientation quaternion of this fiducial, angle and axis, default 0,0,0,1 (or 0,0,0,0,0,0,1.0)<br />
*vis = the visibility flag for this fiducial, 0 or 1, default 1<br />
*sel = the selected flag for this fiducial, 0 or 1, default 1<br />
*lock = the locked flag for this fiducial, 0 or 1, default 0<br />
*label = the name for this fiducial, displayed beside the glyph, with quotes around it if there is a comma in the field<br />
*desc = a string description for this fiducial, optional<br />
*associatedNodeID = an id of a node in the scene with which the fiducial is associated, for example the volume or model on which the fiducial was placed, optional<br />
<br />
The file can be parsed using stringstream and getline:<br />
<br />
std::fstream fstr;<br />
fstr.open(fileName.c_str(), std::fstream::in);<br />
char line[1024];<br />
while (fstr.good())<br />
{<br />
fstr.getline(line, 1024);<br />
std::stringstream ss(line);<br />
std::string component;<br />
getline(ss, component, ',');<br />
[...]<br />
}<br />
<br />
<!-- ---------------------------- --><br />
{{documentation/{{documentation/version}}/module-footer}}<br />
<!-- ---------------------------- --></div>Dzenanzhttps://www.slicer.org/w/index.php?title=Documentation/4.4/Modules/Markups&diff=43097Documentation/4.4/Modules/Markups2015-10-28T16:56:16Z<p>Dzenanz: Adding pointfile</p>
<hr />
<div><noinclude>{{documentation/versioncheck}}</noinclude><br />
<!-- ---------------------------- --><br />
{{documentation/{{documentation/version}}/module-header}}<br />
<!-- ---------------------------- --><br />
<br />
<!-- ---------------------------- --><br />
{{documentation/{{documentation/version}}/module-section|Introduction and Acknowledgements}}<br />
{{documentation/{{documentation/version}}/module-introduction-start|{{documentation/modulename}}}}<br />
{{documentation/{{documentation/version}}/module-introduction-row}}<br />
This work is part of the National Alliance for Medical Image Computing (NA-MIC), funded by the National Institutes of Health through the NIH Roadmap for Medical Research, Grant U54 EB005149. Information on NA-MIC can be obtained from the [http://www.na-mic.org/ NA-MIC website].<br><br />
Author: Nicole Aucoin, SPL, BWH<br><br />
Contact: Nicole Aucoin, <email>nicole@bwh.harvard.edu</email><br><br />
{{documentation/{{documentation/version}}/module-introduction-row}}<br />
{{documentation/{{documentation/version}}/module-introduction-logo-gallery<br />
|{{collaborator|logo|spl}}|{{collaborator|longname|spl}}<br />
}}<br />
{{documentation/{{documentation/version}}/module-introduction-end}}<br />
<br />
<!-- ---------------------------- --><br />
{{documentation/{{documentation/version}}/module-section|Module Description}}<br />
This module is used to manage markups in Slicer. <br />
<br />
It is based on the functionality in the Slicer 3.6 Fiducials module, with elements from the Slicer 4.2 Annotations module. <br />
<br />
Currently, only fiducials are supported.<br />
<br />
{{documentation/{{documentation/version}}/module-description}}<br />
<br />
Development history can be found in the [[documentation/{{documentation/version}}/Modules/{{documentation/modulename}}#References|references]].<br />
<br />
<!-- ---------------------------- --><br />
{{documentation/{{documentation/version}}/module-section|Use Cases}}<br />
<br />
This module allows you to add new markups, edit markups in a list, delete markups and modify display properties of markups.<br />
<br />
Markups can be passed to command line modules (CLIs) using the <pre>--point</pre> or <pre>--pointfile</pre> argument.<br />
<br />
The Mouse mode tool bar can be used to place new markups by positioning the mouse and left clicking in the viewers. Once you are in Place mode and placing Markups fiducials, you can use the 'p' key to place a new fiducial while the Markups GUI is open. You can also use a right button click to stop placing fiducials when in persistent mode.<br />
<br />
The Markups module can convert Annotation fiducials into Markups fiducials, and will offer the user the option of doing it automatically when the Markups GUI is entered. There is also a manual button under the Advanced tab.<br />
<br />
<!-- ---------------------------- --><br />
{{documentation/{{documentation/version}}/module-section|Tutorials}}<br />
<br />
N/A<br />
<br />
<!-- ---------------------------- --><br />
<br />
== Panels ==<br />
<br />
<gallery><br />
File:Markups-GUI-Collapsed.png | GUI<br />
File:Markups-ModifyList.png | Select the active list, set visibility and lock flags, set uniform scale.<br />
File:Markups-JumpSlices-44.png | Jump the 2D slice view windows to the coordinates of the highlighted markup.<br />
File:Markups-ModifyMarkupsInList.png | Modify Markups in list: toggle visibility, selected, and lock flags, delete.<br />
File:Markups-Table.png | Table showing the fiducials in the active Markups list<br />
File:Markups-Advanced.png | Advanced panel: move markups up or down, add a markup, name format, convert annotation fiducials, display properties for the list of markups<br />
File:Markups-DisplayProperties.png | Display properties, inside the Advaned panel, includes 2d projection settings.<br />
File:Markups-MouseToolbar.png | Place Markup fiducials by setting the Mouse mode in the tool bar<br />
</gallery><br />
{{documentation/{{documentation/version}}/module-section|Panels and their use}}<br />
<br />
{{documentation/{{documentation/version}}/module-parametersdescription}}<br />
<br />
<!-- ---------------------------- --><br />
{{documentation/{{documentation/version}}/module-section|Similar Modules}}<br />
<br />
*[[Documentation/Nightly/Modules/Annotations | Annotations]] module is similar, and will be deprecated as the functionality is moved to the Markups module.<br />
*[https://github.com/Slicer/Slicer/blob/master/Modules/Scripted/Endoscopy/Endoscopy.py#L303 Endoscopy module] uses fiducials<br />
<br />
<!-- ---------------------------- --><br />
{{documentation/{{documentation/version}}/module-section|References}}<br />
<br />
*[http://wiki.na-mic.org/Wiki/index.php/2013_Summer_Project_Week:MarkupsModuleSummer2013 Summer 2013 Project Week project page]<br />
*[http://wiki.na-mic.org/Wiki/index.php/2013_Project_Week:MarkupsModule Winter 2013 Project Week project page]<br />
*[http://wiki.na-mic.org/Wiki/index.php/2012_Summer_Project_Week:AnnotationModule Summer 2012 Project Week project page]<br />
<br />
<!-- ---------------------------- --><br />
{{documentation/{{documentation/version}}/module-section|Information for Developers}}<br />
{{documentation/{{documentation/version}}/module-developerinfo}}<br />
<br />
== Python ==<br />
<br />
=== Load From File ===<br />
<br />
Markups fiducials can be loaded from file:<br />
<br />
slicer.util.loadMarkupsFiducialList('/path/to/list/F.fcsv')<br />
<br />
=== Adding Fiducials Programatically ===<br />
<br />
Markups fiducials can be added to the currently active list from the python console by using the following module logic command:<br />
<br />
slicer.modules.markups.logic().AddFiducial()<br />
<br />
The command with no arguments will place a new fiducial at the origin. You can also pass it an initial location:<br />
<br />
slicer.modules.markups.logic().AddFiducial(1.0, -2.0, 3.3)<br />
<br />
=== Adding Fiducials via Mouse Clicks ===<br />
<br />
You can also set the mouse mode into Markups fiducial placement by calling:<br />
<br />
placeModePersistence = 1<br />
slicer.modules.markups.logic().StartPlaceMode(placeModePersistence)<br />
<br />
A lower level way to do this is via the selection and interaction nodes:<br />
selectionNode = slicer.mrmlScene.GetNodeByID("vtkMRMLSelectionNodeSingleton")<br />
selectionNode.SetReferenceActivePlaceNodeClassName("vtkMRMLMarkupsFiducialNode")<br />
interactionNode = slicer.mrmlScene.GetNodeByID("vtkMRMLInteractionNodeSingleton")<br />
placeModePersistence = 1<br />
interactionNode.SetPlaceModePersistence(placeModePersistence)<br />
# mode 1 is Place, can also be accessed via slicer.vtkMRMLInteractionNode().Place<br />
interactionNode.SetCurrentInteractionMode(1)<br />
<br />
=== Access to Fiducial Properties ===<br />
<br />
Each vtkMRMLMarkupsFiducialNode has a vector of points in it which can be accessed from python:<br />
<br />
fidNode = getNode("vtkMRMLMarkupsFiducialNode1")<br />
n = fidNode.AddFiducial(4.0, 5.5, -6.0)<br />
fidNode.SetNthFiducialLabel(n, "new label")<br />
# each markup is given a unique id which can be accessed from the superclass level<br />
id1 = fidNode.GetNthMarkupID(n)<br />
# manually set the position<br />
fidNode.SetNthFiducialPosition(n, 6.0, 7.0, 8.0)<br />
# set the label<br />
fidNode.SetNthFiducialLabel(n, "New label")<br />
# set the selected flag, only selected = 1 fiducials will be passed to CLIs<br />
fidNode.SetNthFiducialSelected(n, 1)<br />
# set the visibility flag<br />
fidNode.SetNthFiducialVisibility(n, 0) <br />
<br />
You can loop over the fiducials in a list and get the coordinates:<br />
<br />
fidList = slicer.util.getNode('F')<br />
numFids = fidList.GetNumberOfFiducials()<br />
for i in range(numFids):<br />
ras = [0,0,0]<br />
fidList.GetNthFiducialPosition(i,ras)<br />
# the world position is the RAS position with any transform matrices applied<br />
world = [0,0,0,0]<br />
fidList.GetNthFiducialWorldCoordinates(0,world)<br />
print i,": RAS =",ras,", world =",world<br />
<br />
You can also look at the sample code in the [https://github.com/Slicer/Slicer/blob/master/Modules/Scripted/Endoscopy/Endoscopy.py#L287 Endoscopy module] to see how python is used to access fiducials from a scripted module.<br />
<br />
=== VTK Widget access ===<br />
<br />
The Markups 3D fiducial displayable manager can be accessed from python allowing debugging of the vtkSeedWidget:<br />
<br />
fidNode = slicer.mrmlScene.GetNodeByID("vtkMRMLMarkupsFiducialNode1")<br />
lm = slicer.app.layoutManager()<br />
td = lm.threeDWidget(0)<br />
ms = vtk.vtkCollection()<br />
td.getDisplayableManagers(ms)<br />
for i in range(ms.GetNumberOfItems()): <br />
m = ms.GetItemAsObject(i)<br />
if m.GetClassName() == "vtkMRMLMarkupsFiducialDisplayableManager3D":<br />
print i, m.GetClassName()<br />
h = m.GetHelper()<br />
seedWidget = h.GetWidget(fidNode)<br />
seedRepresentation = seedWidget.GetSeedRepresentation()<br />
handleRep2 = seedRepresentation.GetHandleRepresentation(2)<br />
print handleRep2.GetDisplayPosition()<br />
print handleRep2.GetWorldPosition()<br />
<br />
<br />
The Markups 2D fiducial displayable manager can be accessed from python as well to debug the seed widget in 2D. For the Red slice viewer:<br />
<br />
fidNode = slicer.mrmlScene.GetNodeByID("vtkMRMLMarkupsFiducialNode1")<br />
lm = slicer.app.layoutManager()<br />
redWidget = lm.sliceWidget("Red")<br />
redView = redWidget.sliceView()<br />
ms = vtk.vtkCollection()<br />
redView.getDisplayableManagers(ms)<br />
for i in range(ms.GetNumberOfItems()):<br />
m = ms.GetItemAsObject(i)<br />
if m.GetClassName() == "vtkMRMLMarkupsFiducialDisplayableManager2D":<br />
print i, m.GetClassName()<br />
h = m.GetHelper()<br />
seedWidget = h.GetWidget(fidNode)<br />
seedRepresentation = seedWidget.GetSeedRepresentation()<br />
handleRep = seedRepresentation.GetHandleRepresentation(0)<br />
print handleRep.GetDisplayPosition()<br />
<br />
== C++ ==<br />
<br />
===Selection and Interaction===<br />
<br />
For the selection and interaction nodes, make sure that you access the singleton nodes already in the scene (rather than making your own) via (caveat: this call works on displayable managers, you may have to go a different route to get at the application logic):<br />
vtkMRMLApplicationLogic *mrmlAppLogic = this->GetMRMLApplicationLogic();<br />
vtkMRMLInteractionNode *inode = mrmlAppLogic->GetInteractionNode();<br />
vtkMRMLSelectionNode *snode = mrmlAppLogic->GetSelectionNode();<br />
If you can't get at the mrml application logic, you can get the nodes from the scene:<br />
vtkMRMLInteractionNode *interactionNode = vtkMRMLInteractionNode::SafeDownCast(this->GetMRMLScene()->GetNodeByID("vtkMRMLInteractionNodeSingleton"));<br />
<br />
You can then call methods on the nodes or add your own event observers as in<br />
vtkSlicerMarkupsModuleLogic::ObserveMRMLScene<br />
You can see vtkSlicerMarkupsModuleLogic::ProcessMRMLNodesEvents to see how to respond to interaction node changes.<br />
<br />
Slicer4/Base/QTGUI/qSlicerMouseModeToolBar.cxx has a lot of the code you'll need as well, with a slightly different way of getting at the mrml app logic to access the nodes.<br />
<br />
The calls you need to make to switch into placing fiducials with the mouse are:<br />
selectionNode->SetReferenceActivePlaceNodeClassName("vtkMRMLMarkupsFiducialNode");<br />
interactionNode->SetCurrentInteractionMode(vtkMRMLInteractionNode::Place);<br />
If you don't set PlaceModePersistence on the interaction node to 1, the mouse mode/current interaction mode will automatically go back to view transform after one fiducial has been placed, and you just need to reset the current interaction mode for future placing (the active place node class name and ID are persistent).<br />
<br />
=== Programmatic Access to Fiducials ===<br />
<br />
This section explains how to access fiducial nodes added into the scene by the user.<br />
<br />
In your module's Logic class, overwrite SetMRMLSceneInternal to observe the NodeAddedEvent of the scene:<br />
void vtkSlicerMYMODULEModuleLogic::SetMRMLSceneInternal(vtkMRMLScene * newScene)<br />
{<br />
vtkNew<vtkIntArray> events;<br />
events->InsertNextValue(vtkMRMLScene::NodeAddedEvent);<br />
// Optionally you can add more events here, <br />
// such as vtkMRMLScene::NodeRemovedEvent<br />
this->SetAndObserveMRMLSceneEventsInternal(newScene, events.GetPointer());<br />
}<br />
Via the set and observe mrml scene macro, anytime a node is added into the scene, it will trigger the base Logic class method<br />
[http://slicer.org/doc/html/classvtkMRMLAbstractLogic.html#a51fdf00ecfc18c1a1465d4bccb789ef1 vtkMRMLAbstractLogic::ProcessMRMLNodesEvents] which calls the virtual method <code>vtkMRMLAbstractLogic::OnMRMLSceneNodeAdded(vtkMRMLNode* node)</code><br />
Reimplement that method in your logic class:<br />
void vtkSlicerMYMODULEModuleLogic::OnMRMLSceneNodeAdded(vtkMRMLNode* addedNode)<br />
{<br />
vtkMRMLMarkupsFiducialNode* fidNode =<br />
vtkMRMLMarkupsFiducialNode::SafeDownCast(addedNode);<br />
if (fidNode)<br />
{<br />
// here you write what you need to do when a <br />
// fiducial node is added into the scene<br />
}<br />
}<br />
<br />
=== File Format===<br />
<br />
The Markups Fiducial storage node uses a comma separated value file to store the fiducials on disk. The format is:<br />
<br />
A leading comment line giving the Slicer version number:<br />
# Markups fiducial file version = 4.3<br />
A comment line giving the coordinate system, RAS = 0, LPS = 1, IJK = 2 (IJK is currently not supported, RAS is printed instead)<br />
# CoordinateSystem = 0<br />
A comment line explaining the fields in the csv<br />
# columns = id,x,y,z,ow,ox,oy,oz,vis,sel,lock,label,desc,associatedNodeID<br />
Then comes the fiducials, one per line, for example:<br />
vtkMRMLMarkupsFiducialNode_0,1.29,-40.18,60.15,0,0,0,1,1,1,0,F-1,,<br />
*id = a string giving a unique id for this fiducial, usually based on the class name<br />
*x,y,z = the floating point coordinate of the fiducial point<br />
*ow,ox,oy,oz = the orientation quaternion of this fiducial, angle and axis, default 0,0,0,1 (or 0,0,0,0,0,0,1.0)<br />
*vis = the visibility flag for this fiducial, 0 or 1, default 1<br />
*sel = the selected flag for this fiducial, 0 or 1, default 1<br />
*lock = the locked flag for this fiducial, 0 or 1, default 0<br />
*label = the name for this fiducial, displayed beside the glyph, with quotes around it if there is a comma in the field<br />
*desc = a string description for this fiducial, optional<br />
*associatedNodeID = an id of a node in the scene with which the fiducial is associated, for example the volume or model on which the fiducial was placed, optional<br />
<br />
The file can be parsed using stringstream and getline:<br />
<br />
std::fstream fstr;<br />
fstr.open(fileName.c_str(), std::fstream::in);<br />
char line[1024];<br />
while (fstr.good())<br />
{<br />
fstr.getline(line, 1024);<br />
std::stringstream ss(line);<br />
std::string component;<br />
getline(ss, component, ',');<br />
[...]<br />
}<br />
<br />
<!-- ---------------------------- --><br />
{{documentation/{{documentation/version}}/module-footer}}<br />
<!-- ---------------------------- --></div>Dzenanzhttps://www.slicer.org/w/index.php?title=Documentation/Nightly/Developers/SlicerExecutionModel&diff=43096Documentation/Nightly/Developers/SlicerExecutionModel2015-10-28T16:17:47Z<p>Dzenanz: /* XML Schema */ Adding pointfile</p>
<hr />
<div>== Introduction ==<br />
<br />
The Slicer Execution Model is designed to improve the acceptance and productivity of Slicer application developers. The Execution Model provides a simple mechanism for incorporating command line programs as Slicer modules. These command line modules are self-describing, emitting an XML description of its command line arguments. Slicer uses this XML description to construct a GUI for the module.<br />
<br />
=== Types of Slicer Plugins ===<br />
<br />
There are various types of plugins that Slicer supports as command line modules. This variety allows a breadth of integration choices to balance performance and flexibility.<br />
<br />
==== Shared object plugins (dll, so) with global symbols ====<br />
<br />
Shared object plugins with global symbols integrate into Slicer tighter than the Executable plugins. Shared object plugins with global symbols can transfer data directly to/from a MRML scene using standard itk::ImageFileReader and itk::ImageFileWriter (and the ImageIO class provided with Slicer, itk::MRMLIDImageIO). Communicating directly with the MRML scene avoids the overhead of reading and writing images to disk. Slicer looks for a standard entry point to execute the module called ModuleEntryPoint defined as<br />
<br />
int ModuleEntryPoint(int argc, char* argv[]);<br />
<br />
Slicer also looks for the global symbols XMLModuleDescription, ModuleLogoImage, ModuleLogoWidth, ModuleLogoHeight, ModuleLogoPixelSize, and ModuleLogoLength. These global symbols provide the xml module description and data for the module logo. The data types for these symbols are<br />
<br />
char *XMLModuleDescription;<br />
unsigned char *ModuleLogoImage;<br />
int ModuleLogoWidth;<br />
int ModuleLogoHeight;<br />
int ModuleLogoPixelSize;<br />
unsigned long ModuleLogoLength;<br />
<br />
These global symbols are accessed during module discovery. The ModuleLogoImage, ModuleLogoWidth, ModuleLogoHeight, ModuleLogoPixelSize, and ModuleLogoLength are optional.<br />
<br />
=====Controlling what symbols are exported from a shared library module=====<br />
<br />
Shared Object Modules are loaded into Slicer using standard "dlopen" style calls. The ModuleEntryPoint is located within the shared library and cached for later use. The ModuleEntryPoint, however, may not be the only function accessible to Slicer within the shared library. To protect against symbol clash at runtime, all functions and variables accessible within file scope of the shared library should be put in an anonymous namespace.<br />
<br />
<pre><br />
// Use an anonymous namespace to keep class types and function names<br />
// from colliding when module is used as shared object module. Every<br />
// thing should be in an anonymous namespace except for the module<br />
// entry point, e.g. main() or ModuleEntryPoint()<br />
//<br />
namespace {<br />
// functions like DoIt() should be put in the anonymous namespace<br />
template<class T> int DoIt( int argc, char * argv[], const T& targ)<br />
{<br />
}<br />
} // end of anonymous namespace<br />
<br />
<br />
int main(int argc, char* argv[] )<br />
{<br />
}<br />
<br />
</pre><br />
<br />
==== Shared object plugins (dll, so) with entry points ====<br />
<br />
Shared object plugins with entry points integrate into Slicer tighter than the Executable plugins. Shared object plugins with entry points can transfer data directly to/from a MRML scene using standard itk::ImageFileReader and itk::ImageFileWriter (and the ImageIO class provided with Slicer, itk::MRMLIDImageIO). Communicating directly with the MRML scene avoids the overhead of reading and writing images to disk. Slicer looks for standard entry points for executing the module as well as for querying the module for its xml description and logos. The entry points are defined as<br />
<br />
int ModuleEntryPoint(int argc, char* argv[]);<br />
char *GetXMLModuleDescription();<br />
unsigned char *GetModuleLogo()(int *width, int *height, int *pixel_size, unsigned long *bufferLength);<br />
<br />
GetXMLModuleDescription() and GetModuleLogo() are accessed during module discovery. GetModuleLogo() is optional.<br />
<br />
==== Executable plugins with global symbols ====<br />
<br />
Executable plugins with global symbols allow for a single executable to be developed that can be integrated into Slicer 3 or run standalone on a cluster. Plugins of this type are opened but not executed at module discovery time. Slicer 3 looks for the global symbols XMLModuleDescription, ModuleLogoImage, ModuleLogoWidth, ModuleLogoHeight, ModuleLogoPixelSize, and ModuleLogoLength. The data types for these symbols are<br />
<br />
char *XMLModuleDescription;<br />
unsigned char *ModuleLogoImage;<br />
int ModuleLogoWidth;<br />
int ModuleLogoHeight;<br />
int ModuleLogoPixelSize;<br />
unsigned long ModuleLogoLength;<br />
<br />
These global symbols are access during module discovery. ModuleLogoImage, ModuleLogoWidth, ModuleLogoHeight, ModuleLogoPixelSize, ModuleLogoLength are optional.<br />
<br />
==== Executable plugins (exe) with command line options ====<br />
<br />
Executable plugins with command line options are the most flexible type of plugin. This approach allows for legacy applications to be integrated into Slicer using a wrapper around the legacy application. Plugins of this type are executed at module discovery time, passing in the command line argument "--xml". The plugin responds to the "--xml" query by emitting the xml description of the module. The plugin is also executed at module discovery time with a "--logo" command line argument. The plugin responds to the "--logo" query by emitting a logo description. <br />
<br />
This type of plugin allows for legacy applications to be integrated into Slicer. A developer can provide Slicer with a small executable or shell script that responds to the "--xml" and "--logo" command line arguments needed for Slicer 3 integration and otherwise spawns the legacy executable passing down any command line arguments.<br />
<br />
> module.exe --xml<br />
> module.exe --logo<br />
<br />
==== Script plugins with Python ====<br />
<br />
Python scripts are found using the discovery mechanisms for the other plugins. Since Python plugins are fundamentally different from the shared and executable plugins, they are documented on their own [[Documentation/Nightly/Developers/SlicerExecutionModel/Python|page]].<br />
<br />
=== Calling Command Line Modules from Other Code ===<br />
<br />
In addition to the automated GUI that shows up in the Slicer user interface, the Execution Model can be used to encapsulate functionality that is invoked by other parts of the program. For instance, the Editor Module can invoke the Model Maker Module to build models. By invoking a command line module in this way, you get the advantage of background processing with progress reporting. See [[Documentation/Nightly/Developers/SlicerExecutionModel/Programmatic Invocation|this page]] for more details.<br />
<br />
=== Architecture === <br />
[[Image:ExecutionModelPlugins.png|600px|Plugin architecture]]<br />
[[Image:CommandLineModule.png|400px|Module architecture]]<br />
[[Image:ModuleFactory.png|400px|Module factory ]]<br />
[[Image:Class_parser_state_coll_graph.png|400px|Module description]]<br />
<br />
== Module Description ==<br />
<br />
Modules are described using XML. The XML is used to generate the C++ command line code and the GUI for the application.<br />
<br />
=== XML Schema ===<br />
<br />
At a minimum, each module description must contain:<br />
<br />
<?xml version="1.0" encoding="utf-8"?><br />
<executable><br />
<title>A title</title><br />
<description>A description</description><br />
<parameters><br />
At least one parameter<br />
</parameters><br />
</executable><br />
<br />
In the following descriptions of each XML tag, CLP means command line processing and GUI means graphical user interface. Unless otherwise specified, tags are optional.<br />
<br />
; <executable> (required)<br />
<br />
;; <category><br />
:: Classifies the executable (e.g. Filtering, Segmentation). Category can be a ''dot'' separated string. Multiple categories can be given and should be separated by a ''semicolon''.<br />
:: ''for CLP'', not used.<br />
:: ''for GUI'', used on the menu selector to group executables. ''Dot'' separated strings can be used to generate sub-menus. ''Semicolon'' separated strings can be used to create multiple categories.<br />
;; </category><br />
<br />
;; <title> (required)<br />
:: A word or two describing the executable (e.g. Median Filter, Anisotropic Diffusion<br />
:: ''for CLP'', not used.<br />
:: ''for GUI'', used to label the frame containing the GUI for the executable. Also, GUI names for volumes use this label as a prefix.<br />
;; </title><br />
<br />
;; <description> (required)<br />
:: A long description of the executable. Any double quotes will be converted to single quotes.<br />
:: ''for CLP'', appears at the end of the --help.<br />
:: ''for GUI'', appears in the help frame.<br />
;; </description><br />
<br />
;; <version><br />
:: The version of the command line executable. A suggested format is:<br />
::: ''major''.''minor''.''patch''.''build''.''status''<br />
::: where status is<br />
:::: vc: version controlled (pre-alpha), build can be a serial revision number, if any (like svn might have).<br />
:::: a: alpha<br />
:::: b: beta<br />
:::: rc: release candidate<br />
:::: fcs: first customer ship<br />
:: ''for CLP'', reported in response to --version.<br />
:: ''for GUI'', not used.<br />
;; </version><br />
<br />
;; <documentation-url><br />
:: The location of extended documentation for the executable, (e.g. http://www.na-mic.org/foo.html).<br />
;; </documentation-url><br />
<br />
;; <license><br />
:: The type of license or a url containing the license, (e.g. Berkeley, Apache, http://www.slicer.org/copyright/copyright.txt).<br />
:: ''for CLP'', not used.<br />
:: ''for GUI'', may show up in the Help or About section.<br />
;; </license><br />
<br />
;; <contributor><br />
:: The author(s) of the command line executable (e.g. Pieper, Jim Miller).<br />
:: for ''CLP'', appears as part of --help<br />
:: for ''GUI'', may show up in the Help or About section.<br />
;; </contributor><br />
<br />
;; <acknowledgements><br />
:: Acknowledgements for funding agency, employer, colleague, (e.g. This work is part of the National Alliance for Medical Image Computing NAMIC), funded by the National Institutes of Health through the NIH Roadmap for Medical Research, Grant U54 EB005149).<br />
:: for ''CLP'', appears as part of --help<br />
:: for ''GUI'', may show up in the Help of About section.<br />
;; </acknowledgements><br />
<br />
;; <parameters [advanced="true|''false''"]> (required for each group of parameters)<br />
:: Starts a group of parameters.<br />
:: for ''CLP'', not used.<br />
:: for ''GUI'', defines a widget (in tk, a frame) that contains other widgets. If ''advanced'' is true, the frame will be closed initially.<br />
<br />
;;; <label> (required)<br />
::: A short string that summarizes a parameter group, (e.g. I/O, Diffusion)<br />
::: for ''CLP'', not used.<br />
::: for ''GUI'', used to label the frame.<br />
;;; </label><br />
<br />
;;; <description> (required)<br />
::: A short description of the parameter group, (e.g. Input/Output Parameters, Anitostropic Diffusion Parameters). Any double quotes will be converted to single quotes.<br />
::: ''for CLP'', not used.<br />
::: ''for GUI'', used in balloon help for the frame containing the parameter group.<br />
;;; </description><br />
<br />
;;; <integer> | <float> | <double> | <boolean> | <string> | <integer-vector> | <float-vector> | <double-vector> | <string-vector> | <integer-enumeration> | <float-enumeration> | <double-enumeration> | <string-enumeration> | <file> | <directory> | <image [type="''scalar''|''label''|''tensor''|''diffusion-weighted''|''vector''|''model''"]> | <geometry [type="''fiberbundle''|''model''"]> | <point [multiple="''true''|''false''"] [coordinateSystem="''lps''|''ras''|''ijk''"]> | <pointfile [multiple="''true''|''false''"] [coordinateSystem="''lps''|''ras''|''ijk''"]> | <region [multiple="''true''|''false''"] [coordinateSystem="''lps''|''ras''|''ijk''"]><br />
::: The type of the parameter. <br />
:::: The scalar types ('''integer''', '''float''', etc.) correspond to the usual programming language types. <br />
:::: The '''-vector''' types are represented by comma separated values of the scalar type. <br />
:::: The '''-enumeration''' types use the '''<element>''' tag to enumerate choices of the scalar type. <br />
:::: '''<image>''' is a special type that indicates that the parameter is a file name that specifies images. <br />
:::: If the attribute ''multiple'' is "true", multiple arguments are allowed for '''scalar''', '''file''', '''directory''', '''image''', '''geometry''', '''point''', '''pointfile''' and '''region''' parameters. BUG: the automatically built GUI will not support selecting multiple volumes for the '''image''' argument, but they can be passed on the command line. If the parameter has a ''flag'' or ''longflag'', then the flag may be specified multiple times on the command line. The resulting C++ variable will be a std::vector of the scalar type. If the multiple parameter does not have a flag, then multiple arguments can appear on the command line. However, a multiple parameter with no flags must be the last parameter specified.<br />
:::: The attribute ''coordinateSystem'' is allowed for the parameters '''point''', '''pointfile''' and '''region'''. <br />
:::: The attribute ''fileExtensions'' is allowed for '''file''', '''pointfile''', '''image''' and '''geometry'''. fileExtensions can contain a list of comma separated file extensions for optional use by the GUI. <br />
<br />
;;;; <name> (required if longflag is not specified)<br />
:::: The name of a command line argument. If name is not specified, longflag will be used (e.g. conductance, numberOfIterations). The name must be usable as a C++ variable. For example, it CANNOT have spaces or special characters and must start with a letter.<br />
:::: ''for CLP'', the name of the C++ variable.<br />
:::: ''for GUI'', used internally.<br />
;;;; </name><br />
<br />
;;;; <description> (required)<br />
:::: A brief description of the parameter. Any double quotes will be converted to single quotes.<br />
:::: ''for CLP'', describes the parameter for --usage and --help.<br />
:::: ''for GUI'', describes the parameter when the cursor is placed over the widget for the parameter (balloon help).<br />
;;;; </description><br />
<br />
;;;; <label> (required)<br />
:::: A label for parameter (e.g. Dicom Directory, Conductance).<br />
:::: ''for'' CLP, not used.<br />
:::: ''for'' GUI, the label for the parameter widget.<br />
;;;; </label><br />
<br />
;;;; <default><br />
:::: A default value for the parameter. The default must be a type that is compatible with the parameter type. The vector parameters are specified as comma separated values of the atomic parameter type.<br />
:::: ''for CLP'', contains the default for the parameter unless the parameter is a ''boolean''. The default for ''boolean'' parameters is always set to ''false''.<br />
:::: ''for GUI'', contains the default for the parameter.<br />
;;;; </default><br />
<br />
;;;; <flag [alias="a,b"] [deprecatedalias="c,d"]> (not required if longflag is present)<br />
:::: A single character command line flag (e.g. s, W). Can provide "alias"'s (comma separated) if different flags can be used to activate the same parameter. Can provide "deprecatedalias"'s (comma separated) if different flags can be used to set the same parameter but the user should be notified of which "updated" flag to use. Parameters with flags are considered "optional" and do not have be specified or assigned. Parameters with flags allow one to override a default behavior.<br />
:::: ''for CLP'', used as the short flag on the command line.<br />
:::: ''for GUI'', used when running the module.<br />
;;;; </flag><br />
<br />
;;;; <longflag [alias="foo,bar"] [deprecatedalias="garf"]> (not required if flag is present)<br />
:::: A command line flag (e.g. spacing, Watcher). Can provide "alias"'s (comma separated) if different long flags can be used to activate the same parameter. Can provide "deprecatedalias"'s (comma separated) if different long flags can be used to set the same parameter but the user should be notified of which "updated" long flag to use. Parameters with flags are considered "optional" and do not have be specified or assigned. Parameters with flags allow one to override a default behavior.<br />
:::: Note: apparently you can't use hyphens in the longflag, so things like --my-option are not allows. -gcs<br />
:::: ''for CLP'', used as the long flag on the command line.<br />
:::: ''for GUI'', used when running the module.<br />
;;;; </longflag><br />
<br />
;;;; <constraints><br />
:::: Encloses constraints on the value of a non-vector, non-enumerated parameter.<br />
:::: ''for CLP'', not used.<br />
:::: ''for GUI'', if present, a slider will be created using the minimum, maximum and step specified.<br />
<br />
;;;;; <minimum><br />
::::: The minimum allowed value for the parameter. If not specified, the minimum is the smallest possible value for the parameter type.<br />
;;;;; </minimum><br />
<br />
;;;;; <maximum><br />
::::: The maximum allowed value for the parameter. If not specified, the maximum is the largest possible value for the parameter type.<br />
;;;;; </maximum><br />
<br />
;;;;; <step><br />
::::: The increment for the parameter.<br />
;;;;; </step><br />
<br />
;;;; </constraints><br />
<br />
;;;; <channel> (required for file, pointfile, directory and image parameters)<br />
:::: Specifies whether the parameter is an input or output parameter.<br />
:::: ''for CLP'', not used.<br />
:::: ''for GUI'', selects the proper widget for file handling.<br />
;;;; </channel><br />
<br />
;;;; <index> (required if there are no flags specified)<br />
:::: An integer starting at 0, that specifies a command line argument that has no flags.<br />
:::: Note: if you use index for, say, an image, the user must enter some input value into the GUI. If the user does not fill in a value, the plugin is not run at all. However, slicer will "seem" to run it, and no error message is given. -gcs<br />
:::: ''for CLP'', specifies the order of an argument that has no flags.<br />
:::: ''for GUI'', used when running the module.<br />
;;;; </index><br />
<br />
;;;; <enumeration> (required for enumeration parameters)<br />
:::: Encloses elements for the parameter. The parameter is restricted one and only one element.<br />
:::: ''for CLP'', not used.<br />
:::: ''for GUI'', defines a radio button with choices.<br />
<br />
;;;;; <element><br />
::::: Defines the choice. Must be of the proper type for a parameter.<br />
::::: ''for CLP'', not used.<br />
::::: ''for GUI'', used as the label for the raido button.<br />
;;;;; </element><br />
<br />
;;;; </enumeration><br />
<br />
;;; </integer> | </float> | </double> | </boolean> | </string> | </integer-vector> | </float-vector> | </double-vector> | </string-vector> | </integer-enumeration> | </float-enumeration> | </double-enumeration> | </string-enumeration> | </file> | </directory> | </image> | </geometry> | </point> | </pointfile> | </region><br />
;; </parameters><br />
<br />
; </executable><br />
<br />
== Slicer GUI Generation ==<br />
<br />
Slicer generates GUI's for each executable discovered during the startup process. Slicer searches directories stored in the Slicer Module Path. This path is set from the Slicer application in View->Application Settings->Modules->Additional module paths. Slicer attempts to run every executable in the prescribed directories and look for a valid XML file in response to a "--xml" command line.<br />
<br />
Here are a few representative examples.<br />
<br />
=== A tour of the Execution Model XML ===<br />
<br />
This example is a sampler of the parameters available in the Execution Model.<br />
<br />
<div class="floatright"><span>[[Image:ExectionModelTourGUI.png|[[Image:ExectionModelTourGUI.png]]]]</span></div><br />
<br />
<?xml version="1.0" encoding="utf-8"?><br />
<executable><br />
<category>Tours</category><br />
<title>Execution Model Tour</title><br />
<description><br />
Shows one of each type of parameter.<br />
</description><br />
<version>1.0</version><br />
<documentation-url></documentation-url><br />
<license></license><br />
<contributor>Daniel Blezek</contributor><br />
<br />
<parameters><br />
<label>Scalar Parameters</label><br />
<description><br />
Variations on scalar parameters<br />
</description><br />
<integer><br />
<name>integerVariable</name><br />
<flag>i</flag><br />
<longflag>integer</longflag><br />
<description><br />
An integer without constraints<br />
</description><br />
<label>Integer Parameter</label><br />
<default>30</default><br />
</integer><br />
<label>Scalar Parameters With Constraints</label><br />
<description>Variations on scalar parameters</description><br />
<double><br />
<name>doubleVariable</name><br />
<flag>d</flag><br />
<longflag>double</longflag><br />
<description>An double with constraints</description><br />
<label>Double Parameter</label><br />
<default>30</default><br />
<constraints><br />
<minimum>0</minimum><br />
<maximum>1.e3</maximum><br />
<step>0</step><br />
</constraints><br />
</double><br />
</parameters><br />
<br />
<parameters><br />
<label>Vector Parameters</label><br />
<description>Variations on vector parameters</description><br />
<float-vector><br />
<name>floatVector</name><br />
<flag>f</flag><br />
<description>A vector of floats</description><br />
<label>Float Vector Parameter</label><br />
<default>1.3,2,-14</default><br />
</float-vector><br />
<string-vector><br />
<name>stringVector</name><br />
<longflag>string_vector</longflag><br />
<description>A vector of strings</description><br />
<label>String Vector Parameter</label><br />
<default>"foo",bar,"foobar"</default><br />
</string-vector><br />
</parameters><br />
<br />
<parameters><br />
<label>Enumeration Parameters</label><br />
<description>Variations on enumeration parameters</description><br />
<string-enumeration><br />
<name>stringChoice</name><br />
<flag>e</flag><br />
<longflag>enumeration</longflag><br />
<description>An enumeration of strings</description><br />
<label>String Enumeration Parameter</label><br />
<default>foo</default><br />
<element>foo</element><br />
<element>"foobar"</element><br />
<element>foofoo</element><br />
</string-enumeration><br />
</parameters><br />
</executable><br />
<br />
=== Module with an integer-vector, one input image and one output image ===<br />
<br />
Here is the XML that describes the MedianImageFilter. The image on the right shows the generated Slicer 3 GUI. The help frame has been expanded by the user.<br />
<br />
<div class="floatright"><span>[[Image:MedianFilterGUI.png|[[Image:MedianFilterGUI.png]]]]</span></div><br />
<pre><br />
<?xml version="1.0" encoding="utf-8"?><br />
<executable><br />
<category><br />
Filtering.Denoising<br />
</category><br />
<title><br />
Median Filter<br />
</title><br />
<description><br />
The MedianImageFilter is commonly used as a robust approach for<br />
noise reduction. This filter is particularly efficient against<br />
"salt-and-pepper" noise. In other words, it is robust to the presence<br />
of gray-level outliers. MedianImageFilter computes the value of each output<br />
pixel as the statistical median of the neighborhood of values around the<br />
corresponding input pixel.<br />
</description><br />
<version>0.1.0.$Revision: 2085 $(alpha)</version><br />
<documentation-url></documentation-url><br />
<license></license><br />
<contributor>Bill Lorensen</contributor><br />
<acknowledgements>This command module was derived from<br />
Insight/Examples/Filtering/MedianImageFilter (copyright) Insight Software Consortium<br />
</acknowledgements><br />
<parameters><br />
<label>Median Filter Parameters</label><br />
<description>Parameters for the median filter</description><br />
<br />
<integer-vector><br />
<name>neighborhood</name><br />
<longflag>--neighborhood</longflag><br />
<description>The size of the neighborhood in each dimension</description><br />
<label>Neighborhood Size</label><br />
<default>1,1,1</default><br />
</integer-vector><br />
<br />
</parameters><br />
<br />
<parameters><br />
<label>IO</label><br />
<description>Input/output parameters</description><br />
<image><br />
<name>inputVolume</name><br />
<label>Input Volume</label><br />
<channel>input</channel><br />
<index>0</index><br />
<description>Input volume to be filtered</description><br />
</image><br />
<image><br />
<name>outputVolume</name><br />
<label>Output Volume</label><br />
<channel>output</channel><br />
<index>1</index><br />
<description>Output filtered</description><br />
</image><br />
</parameters><br />
<br />
</executable><br />
</pre><br />
<br />
=== Module with a multiple scalars, one Input image and one output image ===<br />
<br />
A module with<br />
<br />
<div class="floatright"><span>[[Image:AnisotropicDiffusionFilterGUI.png|[[Image:AnisotropicDiffusionFilterGUI.png]]]]</span></div><br />
<br />
<?xml version="1.0" encoding="utf-8"?><br />
<executable><br />
<category>filtering</category><br />
<title>Anisotropic Diffusion</title><br />
<description><br />
Runs anisotropic diffusion on a volume<br />
</description><br />
<version>1.0</version><br />
<documentation-url></documentation-url><br />
<license></license><br />
<contributor>Bill Lorensen</contributor><br />
<br />
<parameters><br />
<label><br />
Anisotropic Diffusion Parameters<br />
</label><br />
<description><br />
Parameters for the anisotropic<br />
diffusion algorithm<br />
</description><br />
<br />
<double><br />
<name>conductance</name><br />
<longflag>conductance</longflag><br />
<description>Conductance</description><br />
<label>Conductance</label><br />
<default>1</default><br />
<constraints><br />
<minimum>0</minimum><br />
<maximum>10</maximum><br />
<step>.01</step><br />
</constraints><br />
</double><br />
<br />
<double><br />
<name>timeStep</name><br />
<longflag>timeStep</longflag><br />
<description>Time Step</description><br />
<label>Time Step</label><br />
<default>0.0625</default><br />
<constraints><br />
<minimum>.001</minimum><br />
<maximum>1</maximum><br />
<step>.001</step><br />
</constraints><br />
</double><br />
<br />
<integer><br />
<name>numberOfIterations</name><br />
<longflag>iterations</longflag><br />
<description>Number of iterations</description><br />
<label>Iterations</label><br />
<default>1</default><br />
<constraints><br />
<minimum>1</minimum><br />
<maximum>30</maximum><br />
<step>1</step><br />
</constraints><br />
</integer><br />
<br />
</parameters><br />
<br />
<parameters><br />
<label>IO</label><br />
<description>Input/output parameters</description><br />
<image><br />
<name>inputVolume</name><br />
<label>Input Volume</label><br />
<channel>input</channel><br />
<index>0</index><br />
<description>Input volume to be filtered</description><br />
</image><br />
<image><br />
<name>outputVolume</name><br />
<label>Output Volume</label><br />
<channel>output</channel><br />
<index>1</index><br />
<description>Output filtered</description><br />
</image><br />
</parameters><br />
<br />
</executable><br />
<br />
<br />
=== Module with output text presented in GUI ===<br />
<br />
Slicer auto generates GUI to display output from the command line module after execution is complete.<br />
The flag [--returnparameterfile <file name>] is used to provide a name of a file that the module uses to store key=value pairs that are displayed in the Slicer GUI. The keys are described in the command line module xml schema using the output channel.<br />
<br />
This example demonstrates a portion of an XML schema containing 1 string-enumeration input and 7 string outputs.<br />
<br />
<div class="floatright"><span>[[Image:RadnosticsBetaOsteoporosisAnalysisGUI.png|[[Image:RadnosticsBetaOsteoporosisAnalysisGUI.png]]]]</span></div><br />
<br />
<parameters><br />
<label>Radnostics Beta Osteoporosis Analysis</label><br />
<string-enumeration><br />
<name>Osteoporosis</name><br />
<longflag>--osteoporosis</longflag><br />
<label>After Segmentation Perform Osteoporosis Analysis</label><br />
<description>Osteoporosis Analysis will provide a Radnostics Osteoporosis score for the patient.</description><br />
<default>yes</default><br />
<element>yes</element><br />
<element>no</element><br />
</string-enumeration><br />
<string><br />
<name>PatientGender</name><br />
<label>Gender</label><br />
<channel>output</channel><br />
<description>Patient Gender</description> <br />
<default>na</default><br />
</string> <br />
<string><br />
<name>PatientAge</name><br />
<label>Age</label><br />
<channel>output</channel><br />
<description>Patient Age</description> <br />
<default>na</default><br />
</string> <br />
<string><br />
<name>RadnosticsOsteoporosisScore</name><br />
<label>Radnostics Osteoporosis Score</label><br />
<channel>output</channel><br />
<description>Radnostics Osteoporosis Score</description> <br />
<default>na</default><br />
</string> <br />
<string><br />
<name>OsteoporosisComment1</name><br />
<channel>output</channel><br />
<label>*</label><br />
<description>Comment 1</description><br />
</string><br />
<string><br />
<name>OsteoporosisComment2</name><br />
<channel>output</channel><br />
<label>*</label><br />
<description>Comment 2</description><br />
</string><br />
<string><br />
<name>OsteoporosisComment3</name><br />
<channel>output</channel><br />
<label>*</label><br />
<description>Comment 3</description><br />
</string><br />
<string><br />
<name>OsteoporosisComment4</name><br />
<channel>output</channel><br />
<label>*</label><br />
<description>Comment 4</description><br />
</string><br />
</parameters><br />
<br />
The command line module outputs key=value pairs to the file supplied by –-returnparameterfile as shown below:<br />
PatientGender = F <br />
PatientAge = 059Y<br />
RadnosticsOsteoporosisScore = 8 (high)<br />
OsteoporosisComment1 = vertebra label 58 has biconcave fracture<br />
OsteoporosisComment2 = vertebra label 56 has mean density of 120 HU<br />
<br />
<br />
<br />
== Command Line Parsing ==<br />
<br />
The Slicer Execution Model has support for parsing executable command lines. The C++ code to parse the command line arguments is generated automatically from the same XML description that generates the GUI. ''GenerateCLP,'' located in ''SlicerExecutionModel/GenerateCLP'' reads the XML Module Description and creates an include file ''"Executable"CLP.h'' in the build tree. The executable includes this header file and accesses the code with the macro PARSE_ARGS.<br />
<br />
GenerateCLP provides the following to the executable:<br />
<br />
# A brief usage command if required arguments are missing<br />
# A full help command if ''-h'' or ''--help'' is specified on the command line<br />
# A copy of the xml description if ''--xml'' is specified on the command line<br />
# An echo of the command line parameters and their values if ''--echo'' is specified<br />
<br />
GenerateCLP provides the following source code:<br />
<br />
# A C++ declaration of the proper type for each parameter assigning the default value if specified by the XML<br />
# For ''-vector'' parameters, a ''std::vector'' containing the proper C++ type fo the parameter. The generated code parses the comma separated strings to generate the ''std::vector''<br />
<br />
=== Using GenerateCLP ===<br />
<br />
GenerateCLP is normally used via CMake where it is implemented as a CUSTOM_COMMAND. To use GenerateCLP from CMake, you must first include the GenerateCLP package from your CMakeLists.txt file, which will make some macros available to you:<br />
<br />
find_package(SlicerExecutionModel REQUIRED)<br />
include(${SlicerExecutionModel_USE_FILE})<br />
<br />
Note that the HelloSlicer command line module example provides a good starting point and can be used as a skeleton to build your own. It can be found in the [http://na-mic.org/ViewVC/index.cgi/trunk/Modules/CommandLineModule/Testing/HelloSlicer/?sortby=file#dirlist Modules/CommandLineModule/Testing/HelloSlicer] subdirectory.<br />
<br />
For each executable, include the following, replacing '''MyFilter''' with the name of your C++ source:<br />
<br />
set('''MyFilter'''_SOURCE '''MyFilter'''.cxx)<br />
GENERATECLP('''MyFilter'''_SOURCE '''MyFilter'''.xml)<br />
<br />
To generate a stand-alone executable add the lines:<br />
<br />
add_executable('''MyFilter''' ${'''MyFilter'''_SOURCE})<br />
target_link_libraries('''MyFilter''' ${ITK_LIBRARIES})<br />
<br />
Slicer expects modules and plugins to be stored in a specific subdirectory, so that they can be discovered and loaded at run-time. To make sure your CLP module is built in said subdirectory, add the line:<br />
<br />
SEMMacroBuildCLI(<br />
NAME '''MyFilter'''<br />
LOGO_HEADER ${Slicer_SOURCE_DIR}/Resources/NAMICLogo.h<br />
ADDITIONAL_SRCS<br />
'''MyFilter'''.cxx<br />
TARGET_LIBRARIES ModuleDescriptionParser ${ITK_LIBRARIES} vtkTeem MRMLCore SlicerBaseCLI ${VTK_LIBRARIES}<br />
INCLUDE_DIRECTORIES<br />
${vtkTeem_INCLUDE_DIRS}<br />
${MRMLCore_INCLUDE_DIRS}<br />
${vtkITK_INCLUDE_DIRS}<br />
${SlicerBaseCLI_SOURCE_DIR} ${SlicerBaseCLI_BINARY_DIR}<br />
)<br />
<br />
The add_executable target creates a stand-alone executable that can be run from a command line. The add_library target creates a shared library that is discovered at Slicer 3 startup.<br />
<br />
Although this example linked to ITK libraries, other libraries can be specified.<br />
<br />
=== Short Example ===<br />
<br />
This example uses the XML for the [http://www.slicer.org/slicerWiki/index.php/Slicer3:Execution_Model_Documentation#Module_with_an_integer-vector.2C_one_input_image_and_one_output_image|Median Image Filter example].<br />
<br><br />
'''Note:''' The program '''MUST NOT''' write anything to stdout before the ''PARSE_ARGS'' statement. If something is written, the plugin discovery mechanism will not recognize the program as a plugin.<br />
<br />
<nowiki>#include "MedianImageFilterCLP.h"<br />
int main (int argc, char * argv[])<br />
{<br />
PARSE_ARGS;<br />
std::cout << "The size of the neighborhood is: " << neighborhood.size()<br />
<< " and the first element of the neighborhood is: " << neighborhood[0]<br />
<< std::endl;<br />
std::cout << "The input volume is: " << inputVolume << std::endl;<br />
std::cout << "The output volume is: " << outputVolume << std::endl;<br />
return EXIT_SUCCESS;<br />
}<br />
</nowiki><br />
<br />
Here is the output --help:<br />
<br />
<pre><br />
<br />
USAGE: <br />
<br />
./MedianImageFilter [--processinformationaddress <std::string>] [--xml]<br />
[--echo] [--neighborhood <std::vector<int>>] [--]<br />
[--version] [-h] <std::string> <std::string><br />
<br />
<br />
Where: <br />
<br />
--processinformationaddress <std::string><br />
Address of a structure to store process information (progress, abort,<br />
etc.). (default: 0)<br />
<br />
--xml<br />
Produce xml description of command line arguments (default: 0)<br />
<br />
--echo<br />
Echo the command line arguments (default: 0)<br />
<br />
--neighborhood <std::vector<int>><br />
The size of the neighborhood in each dimension (default: 1,1,1)<br />
<br />
--, --ignore_rest<br />
Ignores the rest of the labeled arguments following this flag.<br />
<br />
--version<br />
Displays version information and exits.<br />
<br />
-h, --help<br />
Displays usage information and exits.<br />
<br />
<std::string><br />
(required) Input volume to be filtered<br />
<br />
<std::string><br />
(required) Output filtered<br />
<br />
<br />
The MedianImageFilter is commonly used as a robust approach for noise<br />
reduction. This filter is particularly efficient against<br />
'salt-and-pepper' noise. In other words, it is robust to the presence of<br />
gray-level outliers. MedianImageFilter computes the value of each output<br />
pixel as the statistical median of the neighborhood of values around the<br />
corresponding input pixel.<br />
<br />
Author(s): Bill Lorensen<br />
<br />
Acknowledgements: This command module was derived from<br />
Insight/Examples/Filtering/MedianImageFilter (copyright) Insight<br />
Software Consortium<br />
<br />
<br />
</pre><br />
<br />
=== Parameters and C++ code ===<br />
<br />
This table shows how parameters are defined in the C++ code and how they are specified on the command line.<br />
<br />
{| border="1"<br />
! XML<br />
! C++ Declaration<br />
! Command Line<br />
|-<br />
|<br />
<integer><br /> <name>count</name><br /> <flag>c</flag> </integer><br />
|<br />
int count;<br />
|<br />
''prog'' -c 10<br />
|-<br />
|<br />
<float><br /> <name>stepSize</name><br /> <default>.0625</default><br /> <longflag>stepSize</longflag><br /> </float><br />
|<br />
float stepSize=.0625;<br />
|<br />
''prog'' --stepSize .003<br />
|-<br />
|<br />
<integer multiple="true"><br /> <name>iterations</name><br /> <flag>i</flag><br /> <default>100</default><br /> </integer><br />
|<br />
std::vector<int> iterations;<br /> iterations.push_back(100);<br /><br />
|<br />
''prog'' -i 20 -i 30 -i 100<br />
|-<br />
|<br />
<float-vector><br /> <name>variation</name><br /> <flag>v</flag><br /> <default>1,2,3</default><br /> </float-vector><br />
|<br />
std::vector<float> variation; iterations.push_back(1);<br /> iterations.push_back(2);<br /> iterations.push_back(3);<br /><br />
|<br />
''prog'' -v 10,20,3<br />
|-<br />
|<br />
<string-vector><br /> <name>sites</name><br /> <longflag>sites</longflag> </string-vector><br />
|<br />
std::vector<std::string> sites;<br />
|<br />
''prog'' --sites BWH,GE,Kitware,UNC,MIT,UTAH,GT<br />
|-<br />
|<br />
<string-enumeration><br /> <name>leaders</name><br /> <default>Bill</default><br /> <element>Ron</element><br /> <element>Bill</element><br /> <element>Steve</element><br /> </string-enumeration><br />
|<br />
std::string leaders = "Bill";<br />
|<br />
''prog'' --leaders Ron<br />
|-<br />
|<br />
<boolean><br /> <name>debugSwitch</name><br /> <flag>d</flag> <default>true</default><br /> </boolean><br />
|<br />
bool debugSwitch = true;<br />
|<br />
''prog'' -d<br />
|-<br />
|<br />
<file><br /> <longflag>file1</longflag><br /> <file><br />
|<br />
std::string file1;<br />
|<br />
''prog'' --file1 mytext.txt<br />
|-<br />
|<br />
<image><br /> <name>image</name><br /> <index>0</index><br /> </image><br />
|<br />
std::string image;<br />
|<br />
''prog'' c:/lorensen/Data/ct.nrrd<br />
|-<br />
|<br />
<file multiple="true"><br /> <name>args</name><br /> <index>1</index><br /> </file><br />
|<br />
std::vector<std::string> args;<br />
|<br />
''prog'' --otherFlags file1 file2 ... filen<br />
|-<br />
|<br />
<point multiple="true" coordinateSystem="ras"><br /> <name>seed</name><br /> <longflag>--seed</longflag><br />
|<br />
std::vector<std::vector<float> > seed;<br />
|<br />
''prog'' --seed 10,100,23 --seed 5,240,17<br />
|}<br />
<br />
== Accessing Module Information at Runtime ==<br />
<br />
All of the information contained in the XML description of a module can be accessed at run-time by the command line program. The ''ModuleDescriptionParser'' class library can parse an XML module description and populate a ''ModuleDescription'' instance.<br />
<pre><br />
// Module Description Parser Class Library<br />
#include "ModuleDescriptionParser.h"<br />
#include "ModuleDescription.h"<br />
#include "ModuleParameterGroup.h"<br />
#include "ModuleParameter.h"<br />
.<br />
.<br />
.<br />
// Create a module and a parser<br />
ModuleDescription module;<br />
ModuleDescriptionParser parser;<br />
// Parse the XML<br />
if (parser.Parse(GetXMLModuleDescription(), module))<br />
{<br />
std::cerr << argv[0] << ": One or more XML errors detected." << std::endl;<br />
return EXIT_FAILURE;<br />
}<br />
// Access the module description information<br />
std::cout << "Module Description Information" << std::endl;<br />
std::cout << "\tCategory is: " << module.GetCategory() << std::endl;<br />
std::cout << "\tTitle is: " << module.GetTitle() << std::endl;<br />
std::cout << "\tDescription is: " << module.GetDescription() << std::endl;<br />
std::cout << "\tVersion is: " << module.GetVersion() << std::endl;<br />
std::cout << "\tDocumentationURL is: " << module.GetDocumentationURL() << std::endl;<br />
std::cout << "\tLicense is: " << module.GetLicense() << std::endl;<br />
std::cout << "\tContributor is: " << module.GetContributor() << std::endl;<br />
</pre><br />
<br />
''GetXMLModuleDescription'' is automatically generated by ''GenerateCLP''. Information about parameter groups and parameters is also available [[Accessing_Module_Information_at_Runtime|here]].<br />
<br />
The ''CMakeLists.txt'' file that creates the command line module should point to the ''ModuleDescriptionParser'' library.<br />
<pre><br />
target_link_libraries(${CLP}<br />
ModuleDescriptionParser<br />
)<br />
</pre><br />
<br />
== Error Handling ==<br />
<br />
GenerateCLP attempts to do error checking so that the generated C++ code will compile. These errors will show up as custom command errors during the build process.<br />
<br />
* XML Errors<br />
** ''mismatched tag at line xx'' : The closing tag (a tag with </ >) does not have a matching opening tag.<br />
** ''not well-formed (invalid token) at line xx'' : Probably a blank in the token name.<br />
* ModuleDescriptionParser Errors<br />
** ''<executable> must be the outer most tag''<br />
** ''<executable> was found inside another tag''<br />
** ''<parameters> can only be inside <executable>''<br />
** ''<xxx> can only be used inside <parameters>''<br />
** ''<flag> can only contain one character''<br />
** ''<longname> can only contain letters, numbers and underscores and must start with an _ or letter''<br />
** ''<name> can only contain letters, numbers and underscores and must start with an _ or letter''<br />
<br />
* ModuleDescriptionParser Warnings<br />
** ''<xxx> is an unknown parameter tag'' : Probably a misspelled parameter type.<br />
<br />
* Compiler Errors<br />
** The generated C++ code may have syntax errors if invalid defaults are specified. These will show up during the C++ compilation.<br />
<br />
== Interfacing Legacy Executables ==<br />
<br />
GenerateCLP is only provided as a convenience. Users can use the same XML Module Description to interface C++, shell scripts, tcl programs and even Matlab.<br />
<br />
Example: [http://www.slicer.org/slicerWiki/index.php/Slicer3:FiberTrackingIntegration FiberTracking Integration]<br />
<br />
== Showing Progress in an Application ==<br />
<br />
Programs can communicate progress to the user in two ways. If the program is running an a stand-alone executable, it communicates with a simple XML syntax. If the program is loaded at run-time as a plugin library, it communicates through a C structure.<br />
<br />
The XML syntax is:<br />
<br />
<filter-start><br />
<filter-name><br />
''name of program section or algorithm''<br />
</filter-name><br />
<filter-comment><br />
''description of program section or algorithm''<br />
</filter-comment><br />
</filter-start><br />
<br />
<filter-progress><br />
''floating number from 0 to 1''<br />
</filter-progress><br />
<br />
<filter-end><br />
<filter-name><br />
''name of program section or algorithm''<br />
</filter-name><br />
<filter-time><br />
''execution time''<br />
</filter-time><br />
</filter-end><br />
<br />
The C structure that library plugins use is:<br />
<br />
extern "C" {<br />
struct ModuleProcessInformation<br />
{<br />
/** Inputs from calling application to the module **/<br />
unsigned char Abort;<br />
/** Outputs from the module to the calling application **/<br />
float Progress;<br />
char ProgressMessage[1024];<br />
void (*ProgressCallbackFunction)(void *);<br />
void *ProgressCallbackClientData;<br />
double ElapsedTime;<br />
}<br />
}<br />
<br />
Details on how to use this mechanism are illustrated in [https://github.com/Slicer/Slicer/blob/master/Base/CLI/itkPluginFilterWatcher.h itkPluginFilterWatcher.h].<br />
<br />
For vtk and itk execution model programs, two classes are available that make it simple to add progress. The classes, ''vtkPluginFilterWatcher'' and ''itk::PluginFilterWatcher'' use the vtk and itk command/observer mechanism to report progress.<br />
<br />
vtkPluginFilterWatcher (vtkAlgorithm *'''filter''', const char* '''comment''', ModuleProcessInformation *'''inf''', double '''fraction''', double '''start''') <br /> itk::PluginFilterWatcher (itk::ProcessObject '''filter''', const char* '''comment''', ModuleProcessInformation *'''inf''', double '''fraction''', double '''start''')<br />
<br />
: where:<br />
;; filter <br />
:: is the vtkAlgorithm or itk::ProcessObject to be watched.<br />
;; comment <br />
:: is a string that describes the algorithm.<br />
;; inf <br />
:: is an optional pointer to a structure that is used to communicate with the invoking program when the called program is used as a library plugin. If the pointer is 0, this prgram will not report progress if it is run as a library plugin. Default is 0.<br />
;; fraction <br />
:: is the fraction (0-1) of progress that will be reported by this watcher. This is used when multiple filters are run and each filter represents a proportion of the total progress. Default is 1.<br />
;; start <br />
:: is the offset (0-1) of the progress for this filter. This is added to the progress of the filter. The reported progress of the watched filter is ''start + fraction * filter_progress''.<br />
<br />
<br /> The following example produces progress for a simple vtk program. The variable CLPProcessInformation is automatically declared and set in the program's ''program''CLP.h file.<br />
<br />
<nowiki>#include "vtkPluginFilterWatcher.h"<br />
...<br />
vtkMarchingCubes *cubes = vtkMarchingCubes::New();<br />
cubes->SetInput(reader->GetOutput());<br />
vtkPluginFilterWatcher watchCubes(cubes, "Generate Isosurface", CLPProcessInformation, .5, 0.0);<br />
vtkDecimatePro *decimate = vtkDecimatePro::New();<br />
decimate->SetInput(cubes->GetOutput());<br />
vtkPluginFilterWatcher watchDecimate(decimate, "Reduce Triangle Count", CLPProcessInformation, .5, 0.5);<br />
decimate->Update();<br />
</nowiki><br />
<br />
The following example produces progress for a simple itk program:<br />
<br />
<nowiki>#include "itkPluginFilterWatcher.h<br />
...<br />
typedef itk::MedianImageFilter<ImageType,ImageType> FilterType;<br />
FilterType::Pointer median = FilterType::New();<br />
itk::PluginFilterWatcher watchMedian(median, "Denoise Image", CLPProcessInformation);<br />
</nowiki><br />
<br />
== Adding Module Logos to Slicer ==<br />
Slicer plugins, both libraries and executables, can specify plugin-specific logos. These appear in Slicer when a module is selected. The logos are specified in a .h header file. The KWWidget utility, KWConvertImageToHeader, converts a .png file into a .h header file containing the encoded image and additional information such as width, height and pixel size. [[Documentation-3.5#Requirements_for_Modules|See here for additional information.]]<br />
<br />
For Slicer, execution model plugin logos are stored in Modules/CLI/Resources. The corresponding image in .png format should be stored in Modules/CLI/ImageData. Othere plugins, created outside the Slicer tree, should store the logo and image in a similar location.<br />
<br />
To add a logo to a plugin:<br />
* Create a png image of the logo. The height of the logo should not exceed 40 pixels.<br />
* Convert the logo to the KWWidget icon format as follows. '''NOTE:''' the prefix of the image and header file must be the same for a plugin logo.<br />
<pre><br />
cd Modules/CLI<br />
KWConvertImageToHeader --base64 --zlib Resources/ITKLogo.h ImageData/ITKLogo.png<br />
</pre><br />
* Add the logo to the SEMMacroBuildCLImacro in the CMakeLists.txt file for the plugin using LOGO_HEADER parameter.<br />
<br />
=== Runtime specification of filter types ===<br />
<br />
ITK filters are templated over the images they process. The following code snippet shows how an execution model program can select the image types for filters based on the input images.<br />
<br />
First, include the utilites for plugin's:<br />
<pre><br />
#include "itkPluginUtilities.h"<br />
</pre><br />
<br />
Then, turn your main program into a templated procedure called ''DoIt'':<br />
<pre><br />
template<class T> int DoIt( int argc, char * argv[], T )<br />
{<br />
PARSE_ARGS;<br />
<br />
typedef itk::Image< T, 3 > InputImageType;<br />
typedef itk::Image< T, 3 > OutputImageType;<br />
.<br />
.<br />
.<br />
}<br />
</pre><br />
Then, create a main program that gets the native component type from one of the input file. Here that input file is ''inputVolume'':<br />
<pre><br />
int main( int argc, char * argv[] )<br />
{<br />
<br />
PARSE_ARGS;<br />
<br />
itk::ImageIOBase::IOPixelType pixelType;<br />
itk::ImageIOBase::IOComponentType componentType;<br />
<br />
try<br />
{<br />
itk::GetImageType (inputVolume, pixelType, componentType);<br />
<br />
// This filter handles all types<br />
<br />
switch (componentType)<br />
{<br />
case itk::ImageIOBase::UCHAR:<br />
return DoIt( argc, argv, static_cast<unsigned char>(0));<br />
break;<br />
case itk::ImageIOBase::CHAR:<br />
return DoIt( argc, argv, static_cast<char>(0));<br />
break;<br />
case itk::ImageIOBase::USHORT:<br />
return DoIt( argc, argv, static_cast<unsigned short>(0));<br />
break;<br />
case itk::ImageIOBase::SHORT:<br />
return DoIt( argc, argv, static_cast<short>(0));<br />
break;<br />
case itk::ImageIOBase::UINT:<br />
return DoIt( argc, argv, static_cast<unsigned int>(0));<br />
break;<br />
case itk::ImageIOBase::INT:<br />
return DoIt( argc, argv, static_cast<int>(0));<br />
break;<br />
case itk::ImageIOBase::ULONG:<br />
return DoIt( argc, argv, static_cast<unsigned long>(0));<br />
break;<br />
case itk::ImageIOBase::LONG:<br />
return DoIt( argc, argv, static_cast<long>(0));<br />
break;<br />
case itk::ImageIOBase::FLOAT:<br />
return DoIt( argc, argv, static_cast<float>(0));<br />
break;<br />
case itk::ImageIOBase::DOUBLE:<br />
return DoIt( argc, argv, static_cast<double>(0));<br />
break;<br />
case itk::ImageIOBase::UNKNOWNCOMPONENTTYPE:<br />
default:<br />
std::cout << "unknown component type" << std::endl;<br />
break;<br />
}<br />
}<br />
catch( itk::ExceptionObject &excep)<br />
{<br />
std::cerr << argv[0] << ": exception caught !" << std::endl;<br />
std::cerr << excep << std::endl;<br />
return EXIT_FAILURE;<br />
}<br />
return EXIT_SUCCESS;<br />
}<br />
<br />
</pre><br />
<br />
== Behind the Scenes ==<br />
<br />
A primary goal of the execution model is to relieve developers from developing GUI code and command line parsing code. This section describes the major components of the execution model implementation.<br />
<br />
=== Command Line Processing ===<br />
<br />
Command line processing parses command line arguments and populates internal program variables. Every Unix (and windows) program can receive an argument list through its main entry point. All C and C++ programmers are familiar with the ''int main (int argc, char *[] argv)'' entry point in their programs. Most computer languages including scripting languages provide a similar mechanism to retrieve command line arguments. Simple command line processing directly accesses the strings defined in argv.<br />
<br />
This snippet shows simple commmand line processing:<br />
<br />
int main (int argc, char *argv[])<br />
{<br />
if (argc < 2)<br />
{<br />
std::cout << "Usage: " << argv[0] << " filename" << std::endl;<br />
return -1;<br />
}<br />
std::cout << "The File is " << argv[1] << std::endl;<br />
return 0;<br />
}<br />
<br />
The simple approach works great for a small number of arguments. But larger numbers of arguments of varying types quickly make the processing code more complex and subject to error, both in coding and usage.<br />
<br />
int main (int argc, char *argv[])<br />
{<br />
if (argc < 5)<br />
{<br />
std::cout << "Usage: " << argv[0] << " iterations epsilon inputfile outputfile " << std::endl;<br />
return -1;<br />
}<br />
std::string inputfile(argv[3]);<br />
std::string outputfile(argv[4]);<br />
unsigned int iterations = atoi(argv[1]);<br />
float epsilon = atof(argv[2]);<br />
...<br />
return 0;<br />
}<br />
<br />
Adding flags (or options) to the command line makes the program easier to use but places a larger burden on the program developer. Each developer must ''invent'' a command line argument syntax and implement code to parse the command line. Even a simple example of this is too long to include in this description. This code snippet looks for just two command line arguments.<br />
<br />
int main (int argc, char *argv[])<br />
{<br />
if (argc < 3)<br />
{<br />
std::cout << "Usage: " << argv[0] << " [-i iterations] [-e epsilon] inputfile outputfile " << std::endl;<br />
return -1;<br />
}<br />
std::string inputfile;<br />
std::string outputfile;<br />
unsigned int iterations = 10; /* a default */<br />
float epsilon = .001; /* a defualt */<br />
++argc; /* skip program name */<br />
while (argc > 0)<br />
{<br />
if (strcmp(argv[argc], "-i")<br />
{<br />
iterations = atoi(argv[argc+1]);<br />
argc+=2;<br />
continue;<br />
<br />
else if (strcmp(argv[argc], "-e")<br />
{<br />
epsilon = atof(argv[argc+1]);<br />
argc+=2;<br />
continue;<br />
...<br />
}<br />
<br />
The code gets longer and longer as more options are added and must be rewritten every time a new programs is open.<br />
<br />
To solve this complexity issue, people have developed command line argument libraries. There are dozens, if not hundreds, of command line processing tools. For Slicer3 we looked at argument processors in vxl, nrrd, meta, kwsys and tclap. Each has its strengths and weaknesses. We chose [http://tclap.sourceforge.net/ The Templatized C++ Command Line Parser Library], '''TCLAP'''. '''TCLAP''' is implemented in ''include'' files and does not require a separate library build. As you will see later, the particular command line processing tool is, for the most part, transparent to the Slicer3 developer or user.<br />
<br />
But even these libraries require some work to use.<br />
<br />
==== TCLAP ====<br />
<br />
This example uses '''TCLAP''' to process a command line with 10 possible entries:<br />
<br />
int main ( int argc, char* argv[] ) {<br />
//<br />
// Define default values<br />
int HistogramBins = 30;<br />
int RandomSeed = 1234567;<br />
int SpatialSamples = 10000;<br />
float TranslationScale = 100.0;<br />
int Iterations = 200;<br />
int SplineOrder = 3;<br />
double MinimumStepSize = 0.00001;<br />
double MaximumStepSize = 0.005;<br />
bool PrintTransform = false;<br />
string fixedImageFileName;<br />
string movingImageFileName;<br />
string resampledImageFileName;<br />
//<br />
// Setup command line parsing<br />
try<br />
{<br />
TCLAP::CmdLine cl ( "Register2d", ' ', "$Revision: 1.1 $" );<br />
TCLAP::ValueArg<int> HistogramBinsArg ( "b", "histogrambins", "Number of histogram bins", false, 30, "integer", cl );<br />
TCLAP::ValueArg<int> IterationsArg ( "i", "iterations", "Number of Iterations", false, Iterations, "int", cl );<br />
TCLAP::ValueArg<double> MinimumStepSizeArg ( "m", "minstepsize", "Minimum Step Size", false, MinimumStepSize, "double", cl );<br />
TCLAP::ValueArg<double> MaximumStepSizeArg ( "x", "maxstepsize", "Maximum Step Size", false, MaximumStepSize, "double", cl );<br />
TCLAP::ValueArg<int> RandomSeedArg ( "r", "randomseed", "Random Seed", false, RandomSeed, "int", cl );<br />
TCLAP::ValueArg<int> SpatialSamplesArg ( "s", "spatialsamples", "Number of spatial samples", false, SpatialSamples, "int", cl );<br />
TCLAP::ValueArg<int> SplineOrderArg ( "o", "splineorder", "Order of spline for registration", false, SplineOrder, "int", cl );<br />
TCLAP::SwitchArg PrintTransformArg ( "p", "printtransform", "Print the final transform", PrintTransform, cl );<br />
TCLAP::ValueArg<float> TranslationScaleArg ( "t", "translationscale", "Translation scale", false, TranslationScale, "float", cl );<br />
TCLAP::UnlabeledValueArg<string> FixedImageArg ( "fixed", "Fixed image filename", "", "string", cl );<br />
TCLAP::UnlabeledValueArg<string> MovingImageArg ( "moving", "Moving image filename", "", "string", cl );<br />
TCLAP::UnlabeledValueArg<string> ResampledImageArg ( "resampled", "Resampled image filename", "", "string", cl );<br />
//<br />
// Parse the command line<br />
cl.parse ( argc, argv );<br />
//<br />
// Access the variables<br />
HistogramBins = HistogramBinsArg.getValue();<br />
Iterations = IterationsArg.getValue();<br />
MinimumStepSize = MinimumStepSizeArg.getValue();<br />
MaximumStepSize = MaximumStepSizeArg.getValue();<br />
RandomSeed = RandomSeedArg.getValue();<br />
SpatialSamples = SpatialSamplesArg.getValue();<br />
TranslationScale = TranslationScaleArg.getValue();<br />
PrintTransform = PrintTransformArg.getValue();<br />
fixedImageFileName = FixedImageArg.getValue();<br />
movingImageFileName = MovingImageArg.getValue();<br />
resampledImageFileName = ResampledImageArg.getValue();<br />
}<br />
catch ( ArgException e )<br />
{<br />
cerr << "error: " << e.error() << " for arg " << e.argId() << endl;<br />
exit ( EXIT_FAILURE );<br />
}<br />
<br />
You do get a lot for your investment here. Good error handling and help.<br />
<br />
=== Module Description Parser ===<br />
The XML parsing is done by the [https://github.com/Slicer/SlicerExecutionModel/tree/master/ModuleDescriptionParser ModuleDescriptionParser] class library. ''GenerateCLP'' and Slicer3 use this class library to parse the module XML descriptions. The class ''ModuleDescriptionParser'' has one method, '''Parse''', that converts the XML description into an object model. The resulting object model has one ''ModuleDescription'', one or more ''ModuleParameterGroup'' each of which has one or more ''ModuleParameter''. Each instance has access methods to retrieve information from the XML.<br />
* '''ModuleDescriptionParser''' - parser for command line module XML description.<br />
*: ''Parse(std::string xml, ModuleDescription module)'' - parse an xml string and populate a ModuleDescription.<br />
* '''ModuleDescription''' - contains information about a module <br />
*: const std::string ''GetCategory()'' : returns the contents of '''<category>'''.<br />
*: const std::string ''GetTitle()'' : returns the contents of '''<title>'''.<br />
*: const std::string ''GetDescription()'' : returns the contents of '''<description>'''.<br />
*: const std::string ''GetVersion()'' : returns the contents of '''<version>'''.<br />
*: const std::string ''GetDocumentationURL()'' : returns the contents of '''<documentationURL>'''.<br />
*: const std::string ''GetLicense()'' : returns the contents of '''<license>'''.<br />
*: const std::string ''GetContributor()'' : returns the contents of '''<contributor>'''.<br />
*: const std::vector<ModuleParameterGroup>& ''GetParameterGroups()'' : returns a vector of parameter groups.<br />
* '''ModuleParameterGroup''' - contains ModuleParameters for each parameter group.<br />
*: const std::string ''GetLabel'' - returns the contents of '''<label>'''.<br />
*: const std::string ''GetDescription()'' - returns the contents of the parameter group's '''<description>'''.<br />
*: const std::string ''GetAdvanced()'' - returns advanced attribute. Either "true" of "false".<br />
* '''ModuleParameter''' - contains information for a parameter.<br />
*:GetTag() - returns the parameter's tag, e.g. '''<integer>, <image>''', etc.<br />
*:GetName() - returns the parameter's '''<name>'''.<br />
*:GetLongFlag() - returns the parameter's '''<longflag>'''.<br />
*:GetLabel() - returns the parameter's '''<label>'''.<br />
*:GetMaximum() - returns the parameter's '''<maximum>''' constraint.<br />
*:GetMinimum() - returns the parameter's '''<minimum>''' constraint.<br />
*:GetStep() - returns the parameter's '''<step>'''.<br />
*:GetDescription() - returns the parameter's '''<description>'''.<br />
*:GetChannel() - returns the parameter's '''<channel>'''.<br />
*:GetIndex() - returns the parameter's '''<index>'''.<br />
*:GetDefault() - returns the parameter's '''<default>'''.<br />
*:GetFlag() - returns the parameter's '''<flag>'''.<br />
*:GetMultiple() - returns the parameter's multiple attribute, either "true" or "false".<br />
*:GetCoordinateSystem() - returns the parameter's coordinate system attribute, one of "lps", "ras", or "ijk".<br />
<br />
= Adding a new parameter type =<br />
<br />
Adding a ''new parameter type'' to the execution model involves several modifications:<br />
<br />
# A new XML tag needs to be defined to represent the new parameter type.<br />
# ModuleDescriptionParser needs to be modified to parse the new parameter tag type and specify how the command line processing code generation is going to represent the parameter type<br />
# CommandLineModuleGUI needs to be modified to construct the appropriate GUI element for the parameter type<br />
# CommandLineModuleLogic needs to be modified to put the parameter type onto the command line and request outputs parameter types be loaded back into Slicer and the MRML tree.<br />
# SlicerApplicationLogic needs to be modified to ingest any output parameter types back into Slicer and the MRML tree.<br />
# Additional modification are needed if the parameter is to be passed directly from the MRML tree without using the filesystem.<br />
# Updating the documentation.<br />
<br />
Simple parameter types can be passed directly on the command line. For instance, scalars, small lists, positions, etc. Complicated parameter types are passed to the module via '''abstract files'''. In some cases, these parameters are actually passed as files, where Slicer reads/write the data to the filesystem. In other cases, the parameters are passed as '''abstract files''' which are really references to within the Slicer memory model but appear to the Command Line Module as being a file. The Command Line Module is written using standard ITK ImageFileReader/ImageFileWriter classes but the ITK ImageIO factory mechanism is used to direct the ImageFileReader/ImageFileWriter to talk directly to the Slicer MRML tree instead of to the filesystem.<br />
<br />
== XML tag ==<br />
<br />
This is by far the easiest of the tasks involved in adding a new parameter type but it should not approached hastily. The XML description of a module is designed to be application agnostic. As such, parameter types should be described abstractly or generically. For instance, '''<geometry>''' tag corresponds to the Slicer model node, the '''<point>''' tag corresponds to the Slicer fiducial node, etc.<br />
<br />
Once the tag name is defined, you need to decide whether the parameter type could or should support the attributes '''multiple''', '''coordinateSystem''', or '''fileExtensions''' or perhaps a new attribute type.<br />
<br />
== Modifying ModuleDescriptionParser ==<br />
<br />
SlicerExecutionModel/ModuleDescriptionParser/ModuleDescriptionParser.cxx contains the code to parse the XML description of a module and represent that module description in C++ data structures. To add a new parameter type, this code needs to be modified.<br />
<br />
Two routines need to be modified in ModuleDescriptionParser, ''startElement()'' and ''endElement()''. For ''startElement()'', a new case block needs to be added for the parameter type. You can start by copying the case block for a similar parameter type. This case block is responsible for processing all the attributes for the tag and managing and reporting any errors. The case block may define the '''CPPType''', the ''ArgType''', and the '''StringToType''' needed for the code generation of the command line parsing. The '''CPPType''' is used by in the generated command line processing code to represent the parameter. This may be a simple C++ type or an STL container. The '''ArgType''' is the canonical type of each component of the parameter. The '''StringToType''' is the name of the method to use to convert the ASCII command line parameter to the final '''ArgType'''. The ''endElement()'' method merely needs a new case block to add the parameter to the description.<br />
<br />
ModuleDescriptionParser is fairly general, handling scalars, lists of scalars, and file types as parameter types. A parameter which does not fit these models will require considerable alterations to the ModuleDescriptionParser as well as the GenerateCLP.<br />
<br />
== Constructing a GUI for a new parameter type ==<br />
<br />
To have a GUI element appear in the module GUI for a new parameter type, Base\QTCLI\qSlicerCLIModuleUIHelper.cxx and Base\QTCLI\vtkSlicerCLIModuleLogic.cxx have to be modified. A new case block needs to be added to the '''BuildGUI()''' method for the new parameter type. This case block is triggered off the XML tag for the parameter type. The case block is responsible for the constructing the appropriate GUI element for the parameter, complete with specifying the label and help text. The design is very simple. A single widget is used for each parameter. If a more complicated GUI is needed with multiple widgets, then perhaps a new widget is needed to encapsulated a set of widgets or a naming convention can be added to manage all the widgets associated with a parameter. The widgets for the parameters are stored in a map, indexed by the '''name''' of the parameter.<br />
<br />
The vtkCommandLineModuleGUI (and vtkCommandLineModuleLogic) are designed to operate very generically with sets of parameters. The aforementioned map of widgets indexed by parameter '''name''' is one such example. The implementation of several of the methods in the vtkCommandLineModuleGUI (and vtkCommandLineModuleLogic) generically iterate over the widget map or over the parameter list. It is important to keep this in mind as new parameters are added. The design goal is to minimize the number of ''special cases'' in the code.<br />
<br />
Note that there may be separate case blocks for '''input''' and '''output''' parameter types.<br />
<br />
== Communicating the new parameter to the Command Line Module ==<br />
<br />
To communicate the new parameter type to and from a Command Line Module, the '''ApplyTask()''' method Slicer3/Modules/CommandLineModule/vtkCommandLineModuleLogic.cxx needs to be modified. <br />
<br />
If the parameter type is communicated to the command line module as a file (as opposed to directly on the command line as a number or srting), then there are several blocks of code to construct a temporary file name, keep track of whether that node needs to be written to the filesystem before execution, read from the filesystem after the execution, and deleted after execution completes. These blocks may need to be modified based on your new parameter type.<br />
<br />
The command line is constructed in two passes. First, a pass is made over the parameter list, building the portion of the command line for any parameters that have flags. Note that whether a parameter has a flag or not is up to the discretion of the module author and is not defined by the parameter type. Second, a pass is made to construction the portion of the command line for the parameters that do not have flags. These parameters are ordered appropriately, then placed on the command line. For parameters with flags, this code emits the flag and the parameter value. For the parameters without flags, this code emits just the parameter value. You will need to edit both of these passes to emit your parameter type. In most cases, this is simply a matter of grabbing the parameter value from the parameter and pushing it onto the command line. But some parameter types do require translation to a string appropriate for the command line.<br />
<br />
== Output parameters from the Command Line Modules ==<br />
<br />
Any outputs from a Command Line Module that are communicated via files are queued to be read by the main application thread. Command Line Modules run in a separate execution thread from the main GUI. This thread is not allowed to modify the Slicer GUI, so any results from the module that need to be read back into Slicer and displayed are queued for the main thread.<br />
<br />
The '''ProcessReadData()''' method of Slicer3/Base/Logic/vtkSlicerApplicationLogic.cxx pulls data from the queue to load back into Slicer and display. You may need to a case block for your new parameter type to construct the appropriate storage node and display node.<br />
<br />
== Communicating directly with the MRML tree ==<br />
<br />
Currently scalar image types can sent as parameters to shared object command line modules without going through the filesystem. Slicer provides a new ITK ImageIO factory that can communicated directly with the Slicer MRML tree. This ImageIO factory is in Slicer/Libs/MRMLIDImageIO. This approach can be extended for other image types such as vector or tensor volumes.<br />
<br />
For other constructs such as models and transforms, we'll need to see if an existing factory mechanism can be leverage to communicate directly with the Slicer MRML tree. An alternative may be to construct bridges specific to interfacing from a command line module to the Slicer MRML tree.<br />
<br />
== Adding new image types ==<br />
<br />
The Command Line Module support scalar, vector, tensor, and diffusion weighted images. Adding a new image type to the Command Line Module requires modify the sections of the code outlined above to manage GUI for the module, to construct temporary file names, to write image to disk, and load them back into the MRML tree. These blocks are easy to identify as case blocks on vtkMRMLScalarVolumeNode, vtkMRMLDiffusionTensorVolumeNode, etc. Note that the '''<image>''' tag supports a '''type''' attribute that can scalar, label, vector, tensor, or diffusion-weighted. The case block for '''image''' in the '''startElement()''' method of ModuleDescriptionParser would need to be extended to recognize a new type of image.<br />
<br />
=FAQ=<br />
<br />
==What facilities are available for third party software to interface with the NA-MIC Kit?==<br />
<br />
NA-MIC has committed to developing and supporting a generic batch processing interface. This is the Slicer3 Command Line Interface (CLI). The application programming interface (API) for CLI was worked out based on interactions with the community including input from those familiar with LONI, ITK, Python and Matlab software development practices.<br />
<br />
We believe this API already supports all the features required by to implement batch processing with BatchMake, the LONI Pipeline, and any other systems that we may want to work with in the future. The [[Slicer3:Execution_Model_Documentation|Slicer3 Execution Model]] provides a simple XML-based descriptor for command line arguments for input/output volumes and related parameters.<br />
<br />
==Which Third Party software has currently been interfaced to the NA-MIC Kit using the CLI?==<br />
<br />
*Batchmake (Kitware)<br />
*Python<br />
*Matlab<br />
<br />
== How do I debug my command line module? ==<br />
<br />
See [http://www.slicer.org/slicerWiki/index.php/Slicer3:Execution_Model_Documentation:Debugging Debugging]<br />
<br />
== How do I specify a Logo? ==<br />
<br />
You need to create a logo.h file (embedding the zipped pixel data as base64 strings in a C source code). There is a utility bundled with kwwidgets that accomplishes this. From a Slicer3 build directory you can do something like this:<br />
<br />
./Slicer3 --launch ../Slicer3-lib/KWWidgets-build/bin/KWConvertImageToHeader --base64 --zlib ~/Desktop/bioimagesuite_logo_www.h ~/Desktop/bioimagesuite_logo_www.png<br />
<br />
The resulting .h header file can then be used as an argument to the 'generateclp' cmake macro like this:<br />
<br />
generateclp(${CLP}_SOURCE ${CLP}.xml ${Slicer3_SOURCE_DIR}/Resources/bioimagesuite_logo_www.h)<br />
<br />
''Note: logos are limited in size: if your .h file contains a symbol like 'image_bioimagesuite_logo_www' it will work. But if it has several symbols like 'image_bioimagesuite_logo_www_1' 'image_bioimagesuite_logo_www_2' etc then the logo was too large and needs to be downsampled for use.''<br />
<br />
== What do I do if I want to use the CLI outside of slicer? ==<br />
<br />
In general CLI executables will depend on shared libraries provided by Slicer (such as ITKCommon, etc). To be sure your run-time environment resolves to the correct versions, you should always use a form like:<br />
./Slicer3 --launch <CLI><br />
or, you can start a shell from which you can run multiple CLI modules. Something like:<br />
./Slicer3 --launch xterm &<br />
<br />
We do not suggest hard-coding the runtime paths into, for example, your .bashrc since this may interfere with other programs and can make it hard to use multiple versions of slicer on the same account.<br />
<br />
= Laundry List =<br />
<br />
Use this section to describe any features that are missing, not documented, or require additional work.<br />
<br />
* Comments from Hans Johnson, June 4, 2010:<br />
** Enumerated types can only have about 3 characters in them, otherwise they don’t render in the slicer wizard at all.<br />
** There is no way to unset an enumerated type, and the values are cached even after leaving the module. If the default enumerated type is to be blank, and you select it, then you must restart slicer in order to make the blank option available again.<br />
** There is no way to have conditional options. For example if one flag is used, then other flag can not be used, so it should be disabled.<br />
<br />
* Documenting your module:<br />
** a convenience script has been developed by Hans Johnson, which creates a wiki page describing the command line module by extracting the information from the xml description [[SEMToMediaWiki.py]]. TODO: extract the default values for the parameters to be included in the documentation</div>Dzenanzhttps://www.slicer.org/w/index.php?title=Documentation/4.4/Modules/ResampleVolume&diff=40769Documentation/4.4/Modules/ResampleVolume2015-01-26T19:39:01Z<p>Dzenanz: fixing redirect</p>
<hr />
<div>#REDIRECT [[Documentation/4.4/Modules/ResampleScalarVolume]]</div>Dzenanzhttps://www.slicer.org/w/index.php?title=Documentation/Nightly/Developers/Build_Instructions/Prerequisites&diff=39332Documentation/Nightly/Developers/Build Instructions/Prerequisites2014-10-23T19:46:10Z<p>Dzenanz: /* Windows */</p>
<hr />
<div><noinclude>{{documentation/versioncheck}}</noinclude><br />
__TOC__<br />
<br />
== PREREQUISITES ==<br />
<br><br />
<!--<br />
Please check that the following tools are installed on your machine.<br />
<br />
<br><br />
{{mbox<br />
| type = protection<br />
| text = Qt libraries are '''required'''. Consider reading [[Documentation/{{documentation/version}}/Developers/Build_Instructions/Prerequisites/Qt|Qt requirements]].<br />
| image= [[{{tool|logo|qt}}|x40px]]<br />
}}<br />
<br><br />
<br />
{{mbox<br />
| type = protection<br />
| text = CMake is '''required'''.<br />
| image= [[{{tool|logo|cmake}}|x40px]]<br />
}}<br />
<br><br />
<br />
{{mbox<br />
| type = protection<br />
| text = &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Git is '''required'''.<br />
| image= [[{{tool|logo|git}}|x40px]]<br />
}}<br />
<br><br />
<br />
{{mbox<br />
| type = protection<br />
| text = SVN is '''required'''.<br />
| image= [[{{tool|logo|svn}}|x40px]]<br />
}}<br />
<br />
<br><br />
--><br />
Consider reading platform specific requirements listed below.<br />
<br />
=== Linux ===<br />
* [{{tool|download|cmake}} CMake] >= 2.8.9<br />
* [{{tool|download|git}} Git] >= 1.7.10<br />
* [{{tool|download|svn}} Svn] >= 1.7<br />
* Qt 4.8.6. <small>See details [http://na-mic.org/Mantis/view.php?id=3325#bugnotes here] (Note that any version >= Qt 4.7.4 can be used only for Ubuntu < 12.04)</small><br />
<br />
==== Ubuntu / Debian ====<br />
* Ubuntu, Debian squeeze/wheezy/testing(jessie) users, just type the following line in a terminal<br />
sudo apt-get install subversion git-core git-svn<br />
sudo apt-get install make gcc g++ libX11-dev libXt-dev libgl1-mesa-dev libosmesa6-dev libglu1-mesa-dev libfontconfig-dev libxrender-dev libncurses5-dev<br />
sudo apt-get install cmake<br />
sudo apt-get install qt-sdk<br />
<br />
'''For Ubuntu 12.04, 12.10 and 13.04''': You MUST download and build the standard cmake from http://cmake.org/ because the distributed version of cmake cannot be used to build slicer.<br />
<br />
<pre><br />
sudo apt-get install openssl libssl-dev<br />
<br />
mkdir ~/Support && cd Support # This is where we will download and install required software<br />
<br />
# By copying this one-liner in your terminal, it will download and build CMake<br />
cmake_package="cmake-2.8.11.2"; \<br />
wget http://www.cmake.org/files/v2.8/$cmake_package.tar.gz -v -O $cmake_package.tar.gz && \<br />
tar -xzvf $cmake_package.tar.gz; \<br />
cd $cmake_package && \<br />
cmake -DCMAKE_USE_OPENSSL:BOOL=ON && \<br />
make -j4<br />
<br />
# By copying this one-liner, symbolic links to cmake tools will be created in /usr/local/bin. <br />
# That way calling cmake, ctest, ... from the command line will resolve to this version of CMake.<br />
for tool in cmake ccmake ctest cpack; do sudo ln -s ~/Support/$cmake_package/bin/$tool /usr/local/bin/$tool; done<br />
</pre><br />
<br />
'''For Ubuntu 12.04, 12.10, 13.04 and 13.10''': Qt >= 4.8.5 MUST be used. See reference below for more details.<br />
<br />
<pre><br />
cd ~/Support # This is where we will build Qt and dependent libraries<br />
<br />
# Keep track of our working directory<br />
cwd=$(pwd)<br />
<br />
# This will download, then build zlib and openssl in the current folder<br />
rm -f get-and-build-openssl-for-slicer.sh<br />
wget https://gist.githubusercontent.com/jcfr/9513568/raw/21f4e4cabca5ad03435ecc17ab546dab5e2c1a2f/get-and-build-openssl-for-slicer.sh<br />
chmod u+x get-and-build-openssl-for-slicer.sh <br />
./get-and-build-openssl-for-slicer.sh <br />
<br />
# This will download Qt source in the current folder<br />
wget http://packages.kitware.com/download/item/6175/qt-everywhere-opensource-src-4.8.6.tar.gz<br />
md5=`md5sum ./qt-everywhere-opensource-src-4.8.6.tar.gz | awk '{ print $1 }'` &&<br />
[ $md5 == "2edbe4d6c2eff33ef91732602f3518eb" ] || echo "MD5 mismatch. Problem downloading Qt"<br />
<br />
# This will configure and build Qt in RELEASE against the zlib and openssl previously built<br />
tar -xzvf qt-everywhere-opensource-src-4.8.6.tar.gz<br />
mv qt-everywhere-opensource-src-4.8.6 qt-everywhere-opensource-release-src-4.8.6<br />
mkdir qt-everywhere-opensource-release-build-4.8.6<br />
cd qt-everywhere-opensource-release-src-4.8.6<br />
./configure -prefix $cwd/qt-everywhere-opensource-release-build-4.8.6/ \<br />
-release \<br />
-opensource -confirm-license \<br />
-no-qt3support \<br />
-webkit \<br />
-nomake examples -nomake demos \<br />
-openssl -I $cwd/openssl-1.0.1e/include -L $cwd/openssl-1.0.1e \<br />
&& make -j7 && make install<br />
</pre><br />
<br />
References:<br />
<br />
* Why cmake should be built from Source on Ubuntu 12.04 and above ? <br />
** http://slicer-devel.65872.n3.nabble.com/PYTHON-INCLUDE-DIR2-when-building-Slicer4-td4028557.html<br />
** Slicer issue [http://na-mic.org/Mantis/view.php?id=3103 #3103]<br />
** Slicer issue [http://na-mic.org/Mantis/view.php?id=3116#c8649 #3116]<br />
** CMake issue [http://public.kitware.com/Bug/view.php?id=14156 #14156]<br />
<br />
* Why Qt >= 4.8.5 should be used on Ubuntu 12.04 and above ?<br />
** Slicer issue [http://www.na-mic.org/Bug/view.php?id=3325 #3325]<br />
<br />
==== CentOS ====<br />
*CentOS user type:<br />
yum install make gcc-c++ libX11-devel libXt-devel libXext-devel libGLU-devel mesa-libOSMesa-devel mesa-libGL-devel mesa-libGLU-devel ncurses<br />
<br />
<!--<br />
Todo: This will have to be added in FAQ: Troubleshoot section<br />
''glx-utils'' provides ''glxgears'' that can be used to test rendering<br />
--><br />
<br />
=== MacOSX ===<br />
<br />
'''MacOSX 10.9.4 (Maverick):'''<br />
<br />
''' (1) Make sure to install this update: http://support.apple.com/kb/DL1754'''<br />
<br />
''' (2) Use CMake 3.0.1'''<br />
<br />
* Mac Os X >= 10.5 (Leopard)<br />
* [{{tool|download|cmake}} CMake] >= 2.8.9<br />
** For Mac Os X >= 10.8 ([http://en.wikipedia.org/wiki/OS_X_Mountain_Lion Mountain Lion]) and/or recent XCode >= 4.5.X - [{{tool|download|cmake}} CMake] >= 2.8.11 is required. See http://www.cmake.org/files/v2.8/cmake-2.8.11-Darwin64-universal.tar.gz<br />
<!-- Waiting for the official release, get the release candidate rc1 [http://www.cmake.org/files/v2.8/cmake-2.8.11-rc1-Darwin64-universal.tar.gz here]. For explanation, see [[Documentation/{{documentation/version}}/Developers/Build_Instructions#ld:_framework_not_found_QtWebKit|here]] and [[Documentation/{{documentation/version}}/Developers/Build_Instructions#On_MacOSX_10.8.2C_CMake_hangs_forever|here]]. These versions are also known to work: exact version 20130121-g92bd8 [http://www.cmake.org/files/dev/cmake-2.8.10.20130121-g92bd8-Darwin-universal.tar.gz here] (or version >= 2.8.10.20130220 [http://www.cmake.org/files/dev/?C=M;O=D here]).<br />
--><br />
<br />
$ curl -O http://www.cmake.org/files/v2.8/cmake-2.8.11-Darwin64-universal.tar.gz<br />
$ tar -xzvf cmake-2.8.11-Darwin64-universal.tar.gz --strip-components=1<br />
<br />
$ CMake\ 2.8-11.app/Contents/bin/cmake --version<br />
cmake version 2.8.11<br />
<br />
* [{{tool|download|git}} Git] >= 1.7.10<br />
* [{{tool|download|svn}} Svn] >= 1.7<br />
<!--<br />
*[[{{tool|logo|cmake}}|x16px]] [{{tool|download|cmake}} CMake] >= 2.8.9<br />
*[[{{tool|logo|git}}|x16px]] [{{tool|download|git}} Git] >= 1.6.5<br />
--><br />
* XCode (for the SDK libs)<br />
* Qt >= 4.8.5. We recommend you install the following two packages:<br />
** Download and install [http://download.qt-project.org/official_releases/qt/4.8/4.8.6/qt-opensource-mac-4.8.6-1.dmg qt-opensource-mac-4.8.6-1.dmg]<br />
** Download and install [http://download.qt-project.org/official_releases/qt/4.8/4.8.6/qt-opensource-mac-4.8.6-1-debug-libs.dmg qt-opensource-mac-4.8.6-1-debug-libs.dmg]<br />
** For more details [[Documentation/{{documentation/version}}/Developers/Build_Instructions/Prerequisites/Qt#Mac|here]]<br />
* XQuartz - For Mac Os X >= 10.8 ([http://en.wikipedia.org/wiki/OS_X_Mountain_Lion Mountain Lion]) install XQuartz (http://xquartz.macosforge.org) to get X11 (no longer a default in OS X).<br />
<!--<br />
** Newer Xcode versions (e.g. 4.3.2) use clang as the default compiler and '''clang is not compatible with ITK version 3'''. You should use ITK version 4 with recent versions of Xcode.<br />
** Xcode with gcc should ork with either version of ITK.<br />
** OS X Mountain Lion: In Xcode 4.5 you now need to install command line tools (no longer included by default). Install within Xcode under the Xcode->Preferences->Downloads tab (otherwise git svn will give errors). Then you will need to install XQuartz (http://xquartz.macosforge.org) to get X11 (no longer a default in OS X).<br />
--><br />
<br />
=== Windows ===<br />
* [{{tool|download|cmake}} CMake] >= 2.8.10<br />
* [{{tool|download|git}} Git] >= 1.7.10<br />
<!--<br />
*[[{{tool|logo|cmake}}|x16px]] [{{tool|download|cmake}} CMake] >= 2.8.10<br />
*[[{{tool|logo|git}}|x16px]] [{{tool|download|git}} Git] >= 1.7.10<br />
* [https://code.google.com/p/msysgit/downloads/list?can=3 ''Git-X.X.X-preview2013XXXX.exe''] recommended.<br />
--><br />
<!--<br />
** Use of [http://code.google.com/p/tortoisegit/ TortoiseGit] is optional.<br />
--><br />
** {{note}}For convenience, you could update the PATH variable so that ''git'' can be automatically discovered when configuring Slicer. If not, you will have to specify the GIT_EXECUTABLE at configure time.<br />
<br />
* [http://www.sliksvn.com/en/download SlikSvn] <!-- or [http://www.cygwin.com cygwin's svn client]-->. If using TortoiseSVN (versions that support command line tools), make sure you install Command line tools (disabled by default)<br />
<br />
* NSIS (optional): Needed if packaging Slicer - Click [http://nsis.sourceforge.net/Download here] to download.<br />
<br />
* Qt 4.8.6 (for VS2008). <!--See details [[Documentation/{{documentation/version}}/Developers/Build_Instructions/Prerequisites/Qt#Windows|here]]--><br />
** Normally you should plan to build Qt from source to have complete control over the process. See [https://github.com/jcfr/qt-easy-build one-liner build command] <!-- or more detailed [[#Build_instructions]] --><br />
** You may find that the '''unsigned''' binary downloads from this site are usable: http://sourceforge.net/projects/qtx64/files/qt-x64/4.8.6/msvc2008/ (If you use .7z varaints extract them in the default location C:\Qt\ otherwise that installation will not be properly configured for use with CMake)<br />
** '''Details''': Using the Qt Designer on Windows requires that the Slicer plugins are [http://doc.trolltech.com/4.6/deployment-plugins.html#debugging-plugins|compiled in the same mode as Qt]. If Qt was compiled in Release mode (default), Slicer would have to be compiled in Release mode as well. However if Qt is compiled in Debug mode only, Slicer has to be compiled in Debug too. When Qt is in "Release and Debug" mode only the Release version of Designer.exe is generated, Slicer would have to be compiled in Release. So for developers, the <b>Debug mode is recommended</b>.<br />
<br />
* IDE<br />
** '''Tested/Recommended''': [https://www.microsoft.com/visualstudio/en-us/products/2008-editions Microsoft Visual Studio 9 2008] (Any edition). [http://go.microsoft.com/?linkid=7729279 VS Express 2008 with SP1 direct link] (functional as of Jan 11, 2014)<br />
*** Make sure to install:<br />
**** SP1. Click [http://www.microsoft.com/download/en/details.aspx?id=10986 here] to download SP1 and click [http://massmail.spl.harvard.edu/public-archives/slicer-devel/2012/008206.html here] to understand the motivation.<br />
** '''Experimental''': The plan is to support [https://www.microsoft.com/visualstudio/en-us/products/2010-editions Microsoft Visual Studio 2010] (Any edition). {{note}} NOTE THAT EXPERIMENTAL MEANS THIS DOESN'T YET WORK :)<br />
*** Make sure to build or install:<br />
**** Qt 4.8 <br />
*** Make sure to install:<br />
**** SP1. Click [http://www.microsoft.com/download/en/details.aspx?id=23691 here] to download SP1 and click [http://developer.qt.nokia.com/faq/answer/why_do_all_qt_4.7_applications_crash_when_using_windows_7_x64_w_vs_2010 here] to understand the motivation.<br />
**** SP1 Compiler update. Click [https://www.microsoft.com/download/en/details.aspx?id=4422 here] to download and [http://slicer-devel.65872.n3.nabble.com/Windows-7-64Bits-Slicer-4-Build-with-VC-2010-problem-tt3730524.html#a3731661 here] to understand the motivation.<br />
<!-- JC: This links shouldn't be here: 1) Since this the developer section, it's assumed Visual Studio is installed. 2) The Dll are distributed within Slicer package --><br />
<!-- *** Optional: "Microsoft Visual C++ 2010 SP1 Redistributable Package". Click [http://www.microsoft.com/download/en/details.aspx?id=13523 here for x64] download, or [http://www.microsoft.com/download/en/details.aspx?id=13523 here for x86] download. [http://www.microsoft.com/download/en/details.aspx?displaylang=en&id=29 x86 (32bit) for VS 2008] --><br />
** '''Experimental''' (Visual Studio 2012): Use a desktop version such as [http://www.microsoft.com/visualstudio/eng/downloads#d-express-windows-desktop Visual Studio 2012 Express <b>for Windows Desktop</b>] and remember [http://www.microsoft.com/en-us/download/details.aspx?id=38188 Visual Studio 2012 Update 2]). Do <b>not</b> use Visual Studio Express 2012 <em>for Windows 8</em>. You must install [http://msdn.microsoft.com/en-us/library/ms717422.aspx Windows SDK], otherwise CMake will not even find the C compiler during configure. In CMake, choose <tt>Visual Studio 11 Win64</tt> as generator.<br />
** '''Un-tested''': Cygwin suite (building with cygwin gcc not supported, but the cygwin shell environment can be used to run git, svn, etc).</div>Dzenanz