https://www.slicer.org/w/api.php?action=feedcontributions&user=Jamesobutler&feedformat=atomSlicer Wiki - User contributions [en]2024-03-29T09:26:13ZUser contributionsMediaWiki 1.33.0https://www.slicer.org/w/index.php?title=Documentation/Nightly/Developers/Tutorials/MigrationGuide/Slicer&diff=64330Documentation/Nightly/Developers/Tutorials/MigrationGuide/Slicer2023-03-04T19:46:57Z<p>Jamesobutler: /* Slicer backward incompatible changes */</p>
<hr />
<div><noinclude>__TOC__</noinclude><br />
==Slicer backward incompatible changes==<br />
<br />
===Slicer 5.3: Organization name and domain changed from NA-MIC to Slicer===<br />
<br />
To address the [https://discourse.slicer.org/t/inconsistency-between-organization-name-used-for-settings-files-and-for-macos-bundle-identifier/26547 inconsistency between organization name used for settings files and for macOS bundle identifier], the 3D Slicer application has updated the <code>Slicer_ORGANIZATION_NAME</code> from "NA-MIC" to "Slicer" and <code>Slicer_ORGANIZATION_DOMAIN</code> from <code>www.na-mic.org</code> to <code>www.slicer.org</code>. At the time of this change, 3D Slicer is developed primarily by the Slicer community rather than the NA-MIC community. Distribution of 3D Slicer is hosted at [https://www.slicer.org www.slicer.org] rather than at [https://www.na-mic.org www.na-mic.org].<br />
<br />
Slicer settings will now be under a Slicer directory location rather than NA-MIC. Please review the [https://slicer.readthedocs.io/en/latest/user_guide/settings.html#settings-file-location Settings File Location documentation] for details about the settings location on various platforms.<br />
<br />
===Slicer 5.3: Removed Annotation module===<br />
<br />
Annotations module, which provides `vtkMRMLAnnotationROI` and `vtkMRMLAnnotationRuler` nodes have been deprecated since April 2021 and is to be removed in Slicer-4.3.<br />
<br />
When a scene is loaded into Slicer that contains annotation nodes, they are converted to markup nodes: `vtkMRMLAnnotationROI` is converted to `vtkMRMLMarkupsROI`; and `vtkMRMLAnnotationRuler` is converted to `vtkMRMLMarkupsLine`. All Slicer core modules that previously used annotation nodes, now use markup nodes instead.<br />
<br />
All extensions, too, need to be updated to use markup nodes instead of annotation nodes. For backward compatibility (so that the same extension can be used with current Slicer version and Slicer-4.2 and earlier versions), it is useful to keep the modules accept both markup and annotation nodes, but always create markup nodes by default.<br />
<br />
Tips for updating a module to use markups:<br />
<br />
* In node selectors, wherever `vtkMRMLAnnotationROINode` is accepted, add `vtkMRMLMarkupsLineNode` _before_ it (so they are both accepted, but markups are preferred)<br />
* In node selectors, wherever `vtkMRMLAnnotationRuler` is accepted, add `vtkMRMLMarkupsLine` _before_ it (so they are both accepted, but markups are preferred)<br />
* For ROIs:<br />
** When only non-rotated ROIs are used: you can still use `GetXYZ()` and `GetRadiusXYZ()` methods work the same way for markups ROI<br />
** When ROIs are rotated, markups ROIs support built-in rotation and scaling, therefore it is recommended to use the `exportRoi.GetObjectToWorldMatrix()` method to get all the transforms (including the transform inside the markup node and any transforms applied using transform nodes) that are applied to the bounding box object (that has its center in the origin and its diameter returned by `GetSize()`).<br />
* For rulers:<br />
** Use `GetNthControlPointPosition(0)` and `GetNthControlPointPosition(1)` methods to get the endpoints of the line.<br />
** Use `GetNumberOfDefinedControlPoints()` method to check if both endpoints of the line are defined.<br />
** Use `GetMeasurement('length').GetValue()` to get the line length (or for the displayed string, with units: `getNode('L').GetMeasurement('length').GetValueWithUnitsAsPrintableString()`)<br />
<br />
===Slicer 5.0: API changes since 4.10===<br />
<br />
*Removed protected method <tt>vtkMRMLModelDisplayableManager::FindPickedDisplayNodeFromMesh</tt><br />
<br />
==== Supporting only Python 3.6 and above ====<br />
Slicer python code has been updated to support Python 3.6 and above syntax using [https://github.com/asottile/pyupgrade pyupgrade] to automatically update the syntax.<br />
<br />
Install pyupgrade: <code>PythonSlicer -m pip install pyupgrade</code><br />
<br />
* Running:<br />
** On 1 file: <code>PythonSlicer -m pyupgrade --py36-plus MyPythonFile.py</code><br />
** On multiple files: Here is my pyupgrade-script.py written to automate running pyupgrade across all python files in the Slicer repo. It was run by <code>PythonSlicer pyupgrade-script.py</code><br />
<syntaxhighlight lang="python"><br />
# pyupgrade-script.py<br />
import os<br />
import subprocess<br />
<br />
search_directory = "C:/Users/MyUserName/Documents/GitHub/Slicer"<br />
for root, _, files in os.walk(search_directory):<br />
for file_item in files:<br />
file_path = os.path.join(root, file_item)<br />
if os.path.isfile(file_path) and file_path.endswith(".py"):<br />
subprocess.call(["PythonSlicer", "-m", "pyupgrade", "--py36-plus", file_path])<br />
</syntaxhighlight><br />
<br />
====Python 2 to Python 3====<br />
<br />
Slicer core has been updated to only support Python 3.<br />
<br />
C++ classes and python scripts have been updated to use idioms and constructs only available in Python 3.<br />
<br />
Update to python scripts have been done leveraging the CLI provided by https://python-future.org by (1) iteratively applying each one of the associates "fixes", (2) reviewing associated changes and (3) updating as needed.<br />
<br />
Updates specific to extensions are discussed in [[Documentation/Nightly/Developers/Tutorials/MigrationGuide#Slicer_5.0:_Python2_to_Python3]]<br />
<br />
====Interactor styles====<br />
<br />
Limitations of VTK widgets (editable points, lines, curves, etc.) prevented Slicer from having sophisticated user interaction in slice and 3D views. In Slicer5, we replaced VTK widgets with MRML widgets. These widgets are still VTK-based and somewhat similar to VTK widgets, but they operate directly on MRML nodes, they use direct method calls between widgets and their representation, and they use a more efficient and flexible event processing. Instead of hardcoding how viewers behave in response to interaction (mouse move, button click, keyboard, ...) events in an interactor style, all these events are translated to actions and performed in a MRML widget. Most modules are not expected to observe interactor events or styles directly, but if they did, then they may need to be updated accordingly.<br />
<br />
*vtkSliceViewInteractorStyle renamed to vtkMRMLSliceDViewInteractorStyle to reflect that it uses MRML classes directly.<br />
*vtkThreeDViewInteractorStyle renamed to vtkMRMLThreeDViewInteractorStyle to reflect that it uses MRML classes directly.<br />
<br />
====slicer.util functions====<br />
<br />
*slicer.util.loadVolume (and other node load functions) now return the loaded node instead of a True/False flag. In case of an error, a RuntimeError exception is thrown.<br />
**Old way of loading a node and get it in a variable: <code>volumeNode = slicer.util.loadVolume('path/to/volume.nrrd', returnNode=True)[1]</code><br />
**New way of loading a node and get it in a variable: <code>volumeNode = slicer.util.loadVolume('path/to/volume.nrrd')</code><br />
<br />
====Markups====<br />
<br />
*<tt>vtkCommand::Modified</tt> events are no longer invoked when control points are added/removed/modified to improve performance. Modules that need to know If a point position is modified need to add observers to <tt>vtkMRMLMarkupsNode::PointAddedEvent</tt>, <tt>vtkMRMLMarkupsNode::PointRemovedEvent</tt>, <tt>vtkMRMLMarkupsNode::PointModifiedEvent</tt> events. See example in [https://slicer.readthedocs.io/en/latest/developer_guide/script_repository.html#get-a-notification-if-a-markup-point-position-is-modified Script repository].<br />
*<tt>vtkMRMLMarkupsNode::MarkupAddedEvent</tt> is renamed to <tt>PointPositionDefinedEvent</tt>. There is a similar event, <tt>vtkMRMLMarkupsNode::PointAddedEvent</tt>, which is called even when preview point is created.<br />
*<tt>vtkMRMLMarkupsNode::MarkupRemovedEvent</tt> is renamed to <tt>vtkMRMLMarkupsNode::PointPositionUndefinedEvent</tt>. There is a similar event, <tt>vtkMRMLMarkupsNode::PointRemovedEvent</tt>, which is called even when preview point is removed.<br />
*<tt>vtkMRMLMarkupsNode::NthMarkupModifiedEvent</tt> is replaced by <tt>vtkMRMLMarkupsNode::PointModifiedEvent</tt><br />
*During placement of markups, a preview markup point is created. If number of already placed markup points needs to be determined then <code>GetNumberOfDefinedControlPoints()</code> method can be used.<br />
*<tt>GetDefaultMarkups...()</tt> and <tt>SetDefaultMarkups...()</tt> methods are removed. Instead default display node can be accessed by <tt>GetDefaultMarkupsDisplayNode()</tt> method and default values can be get/set in that class.<br />
*<tt>vtkMRMLMarkupsNode::GetNthMarkupSelected()</tt> is replaced by <tt>GetNthControlPointSelected()</tt><br />
*<tt>vtkMRMLMarkupsNode::PointPositionDefinedEvent</tt> event is added. This event is invoked whenever position is defined for a new point.<br />
*<tt>vtkMRMLMarkupsNode::PointPositionUndefinedEvent</tt> event is added. This event is invoked whenever point with defined position is removed (point is deleted or its position gets undefined).<br />
*For more details, see [{{doxygen-class-url|vtkMRMLMarkupsNode}} vtkMRMLMarkupsNode]<br />
<br />
====Segmentations====<br />
<br />
Binary labelmap segmentations can now be represented as shared labelmaps.<br />
The previous implementation of binary labelmaps was performance intensive as each labelmap was represented using a separate vtkDataObject.<br />
Visualizing and editing segmentations that contained a large number of segments could cause performance issues, due to the large number of vtkActors required, as well as calculating masks and overwriting other segments when editing.<br />
<br />
By default, newly created segments will now be contained on the same layer.<br />
Segments will only be separated into multiple layers if the user creates an overlapping segment when editing.<br />
<br />
Segments are now saved as a 4D volume with shared 3D layers.<br />
For a segmentation that only uses one layer, the resulting image is a 3D volume.<br />
Before saving, the labelmaps will be collapsed into as few layers as possible.<br />
<br />
*seg.nrrd files now contain two additional attributes for each segment: SegmentX_LabelValue and SegmentX_Layer<br />
*The label value of a segment can be found using vtkSegment::GetLabelValue()<br />
*Whether or not a segment is shared can be found using vtkSegmentation::IsSharedBinaryLabelmap()<br />
*The other segments sharing the same labelmap can be found using vtkSegmentation::GetSegmentIDsSharingBinaryLabelmapRepresentation()<br />
*Segment editor effects should generally use modifySelectedSegmentByLabelmap rather than SetBinaryLabelmapToSegment to manage layer separation<br />
*Conversion rules now call PreConvert() and PostConvert() before and after conversion to perform pre and post processing steps on the segmentation as a whole<br />
*The function signature for vtkSegmentationConverterRule::Convert now accepts a vtkSegment rather than two vtkDataObjects<br />
*slicer.util.arrayFromSegment has been deprecated. slicer.util.arrayFromSegmentBinaryLabelmap and slicer.util.arrayFromSegmentInternalBinaryLabelmap can be used instead<br />
<br />
=====Erase the contents of a single segment=====<br />
<br />
<pre><br />
segmentation = segmentationNode.GetSegmentation()<br />
segmentation.ClearSegment(segmentId)<br />
</pre><br />
<br />
=====Set labelmap in a segment=====<br />
<br />
Directly, bypassing masking settings:<br />
<br />
<pre><br />
slicer.vtkSlicerSegmentationsModuleLogic.SetBinaryLabelmapToSegment(orientedImageDataToSet, segmentationNode, segmentId)<br />
</pre><br />
<br />
=====Move a segment from a shared labelmap to a separate layer=====<br />
<br />
<pre><br />
segmentation = segmentationNode.GetSegmentation()<br />
segmentation.SeparateSegmentLabelmap(segmentId)<br />
</pre><br />
<br />
=====Combine all binary labelmaps to as few layers as possible=====<br />
<br />
<pre><br />
segmentation = segmentationNode.GetSegmentation()<br />
segmentation.CollapseBinaryLabelmaps(forceToSingleLayer=false)<br />
</pre><br />
<br />
<b>Get a read-only labelmap for a single segment:</b><br />
<br />
<pre><br />
labelmap = slicer.vtkOrientedImageData()<br />
segmentationNode.GetBinaryLabelmapRepresentation(segmentId, labelmap)<br />
</pre><br />
<br />
(similarly, use GetClosedSurfaceRepresentation with an additional vtk.vtkPolyData parameter to get a read-only surface mesh)<br />
<br />
or<br />
<br />
<pre><br />
labelmapNumpyArray = slicer.util.arrayFromSegmentBinaryLabelmap(segmentationNode, segmentId)<br />
</pre><br />
<br />
=====Get a modifiable shared labelmap=====<br />
<br />
<pre><br />
labelmap = slicer.vtkOrientedImageData()<br />
segmentationNode.GetBinaryLabelmapInternalRepresentation(segmentId, labelmap)<br />
</pre><br />
<br />
(similarly, use GetClosedSurfaceInternalRepresentation to get a modifiable surface mesh)<br />
<br />
or<br />
<br />
<pre><br />
labelmapNumpyArray = slicer.util.arrayFromSegmentInternalBinaryLabelmap(segmentationNode, segmentId)<br />
</pre><br />
<br />
=====Export segments to models=====<br />
<br />
Model hierarchies no longer exist in Slicer5, but instead various kinds of hierarchies are now replaced by "subject hierarchy", which can accommodate any node types in a single hierarchy. Accordingly, `ExportSegmentsToModelHierarchy`, `ExportAllSegmentsToModelHierarchy`, etc. are replaced by `ExportSegmentsToModels`, `ExportAllSegmentsToModels`, which take a subject hierarchy folder item ID as input.<br />
Documentation/Nightly<br />
See code example in [https://slicer.readthedocs.io/en/latest/developer_guide/script_repository.html#export-model-nodes-from-segmentation-node Script repository].<br />
<br />
=====Smoothing effect=====<br />
<br />
In Slicer-4.11 version before October 29, 2020 (and earlier versions), Gaussian smoothing method's Standard deviation parameter ("GaussianStandardDeviationMm") was interpreted in pixels, while on the user interface and code it was claimed to be in physical units (millimeter). The problem was fixed and now the parameter is in millimeter.<br />
<br />
====Volume rendering====<br />
<br />
vtkMRMLVolumeRenderingDisplayNode::SetAndObserveVolumeNodeID method was removed, as display node base class already maintains a pointer to the displayed (volume) node. To associate a volume display node with a volume node, call <pre>volumeNode->AddAndObserveDisplayNodeID(volumeRenderingDisplayNode->GetID());</pre> after both nodes are added to the scene.<br />
<br />
''vtkSlicerVolumeRenderingLogic::CreateDefaultVolumeRenderingNodes'' method unnecessarily polluted the scene with ROI node even though the user did not need cropping. In Slicer-5.x we fixed the issue by not creating the ROI nodes automatically. To create a ROI node, you can call ''vtkSlicerVolumeRenderingLogic::CreateROINode'' method.<br />
<br />
====Extract skeleton====<br />
<br />
Command-line arguments of the module have been updated:<br />
- output image is now optional, therefore the output image file name must be specified using "--outputImage" argument<br />
- output image centerline voxel value is set to 255 (instead of 1) to make it easier to apply image processing operations on it (values can be interpolated between 0 and 255, while there are no integer values between 0 and 1)<br />
- "--dontPrune" is renamed to "--fullTree" for clarity<br />
- centerline curve is saved in mrk.json format<br />
<br />
==== MRML node copy API improvements ====<br />
<br />
Slicer-4.10 and earlier had a single Copy() method, which had limitations:<br />
- usually implemented deep copy (but sometimes bulk data was just shallow-copied): problem, because for quick browsing of sequences, we need shallow-copy (to avoid copying bulk data, such as vtkImageData)<br />
- copied all node properties (except node ID and scene): this required workarounds, whenever we wanted to copy only the content of nodes (but for example keeping node references or node name intact)<br />
<br />
In Slicer-4.11, these limitations are addressed, by implementing a ''CopyContent(vtkMRMLNode* node, bool deepCopy=true)'' method which allows choosing between deep/shallow copy (create an independent copy of bulk data or pass bulk data pointer) and does not copy node ID, Scene, Name, SingletonTag, HideFromEditors, AddToScene, UndoEnabled, and node references.<br />
<br />
To make it easier to introduce this new method into existing classes, helper macros are implemented.<br />
<br />
If a class implements CopyContent method then the developer must make sure that CopyContent and HasCopyContent methods are implemented in all parent classes by adding vtkMRMLCopyContentMacro(ClassName) or vtkMRMLCopyContentDefaultMacro(ClassName) to the class headers. vtkMRMLCopyContentDefaultMacro should be used when the class does not have any additional properties (only those that parent classes already copy). CopyContent must be implemented by calling CopyContent of the parent class, and then copy node properties added in he class (preferable using shallow copy for large data, if deepCopy argument was set to false).<br />
<br />
If HasCopyContent macro is not added to a class then it cannot be recorded or replayed in Sequences module.<br />
<br />
==== Removed classes ====<br />
<br />
Classes removed due to removing legacy Editor module:<br />
* vtkITKNewOtsuThresholdImageFilter is replaced by vtkITKImageThresholdCalculator<br />
* vtkITKGrowCutSegmentationImageFilter is replaced by vtkImageGrowCutSegment (it will be replaced by the ITK implementation https://github.com/Slicer/Slicer/pull/5807)<br />
* vtkITKTimeSeriesDatabase was removed, it was an incomplete class, not used anywhere<br />
* vtkITKWandImageFilter was removed, vtkImageThresholdConnectivity (in VTK) can be used instead<br />
* vtkImageConnectivity was removed, vtkImageThresholdConnectivity (in VTK) can be used instead<br />
* vtkImageErode was removed, vtkImageDilateErode3D (in VTK) can be used instead<br />
* vtkImageLabelChange was removed, vtkImageThreshold (in VTK) can be used instead<br />
* vtkImageSlicePaint was replaced by logic built into qSlicerSegmentEditorPaintEffect<br />
* vtkImageStash is replaced by vtkSegmentationHistory<br />
* vtkPichonFastMarching moved to SegmentEditorExtraEffects extension (https://github.com/lassoan/SlicerSegmentEditorExtraEffects)<br />
<br />
Classes removed due to removing Charts and DoubleArrays modules:<br />
* vtkMRMLChartNode is replaced by vtkMRMLPlotNode<br />
* vtkMRMLChartViewNode is replaced by vtkMRMLPlotViewNode<br />
* vtkMRMLDoubleArrayNode is replaced by vtkMRMLTableNode (can store any number of columns, not just two)<br />
* vtkMRMLDoubleArrayStorageNode is replaced by vtkMRMLTableStorageNode<br />
* qMRMLChartView is replaced by qMRMLPlotView<br />
* qMRMLChartViewControllerWidget is replaced by qMRMLPlotViewControllerWidget<br />
* qMRMLChartWidget is replaced by qMRMLPlotWidget<br />
<br />
===Slicer 5.0: Fiducial List was renamed to Point List===<br />
<br />
To simplify terms used in Slicer, "Fiducial List" term was renamed to "Point List" on the user interface.<br />
The term in the API has not been changed to preserve backward compatibility.<br />
<br />
See discussion of the topic [https://discourse.slicer.org/t/delete-control-point-delete-fiducial-pop-up-confirm-box/20430/18 here].<br />
<br />
===Slicer 5.0: SliceIntersectionVisibility was moved from vtkMRMLSliceCompositeNode to vtkMRMLSliceDisplayNode===<br />
<br />
SliceIntersectionVisibility property (that controls if intersections of other slices should be displayed in the slice view) was stored in vtkMRMLSliceCompositeNode. This was not a good choice because the composite node stores what image layers should be displayed in the slice view and how (what opacity, what blending method, etc.). The property was kept in that class for a long time to preserve backward compatibility, but when interactive slice intersection feature was added and additional properties had to be added that control appearance and behavior of slice intersections, this property was moved into the new vtkMRMLSliceDisplayNode node type and renamed to IntersectingSlicesVisibility.<br />
<br />
Scripts that previously used SliceIntersectionVisibility property will now fail with this error:<br />
<br />
AttributeError: 'MRMLCore.vtkMRMLSliceCompositeNode' object has no attribute 'SetSliceIntersectionVisibility'<br />
<br />
Those failing scripts can be updated with this example in the script repository: https://slicer.readthedocs.io/en/latest/developer_guide/script_repository.html#turn-on-slice-intersections<br />
<br />
===Slicer 5.0: SlicerPython was removed. Use PythonSlicer instead===<br />
<br />
<b>Error message:</b><br />
<br />
<pre><br />
SlicerPython executable is obsolete and will be removed. Use PythonSlicer executable instead.<br />
For more details, see https://github.com/Slicer/Slicer/issues/4843<br />
</pre><br />
<br />
<b>Solution:</b><br />
<br />
Use <tt>PythonSlicer</tt> instead of <tt>SlicerPython</tt><br />
<br />
<b>Background:</b><br />
<br />
Python IDEs (specifically PyCharm, but potentially others) only recognize <tt>Python*.exe</tt> files as Python interpreters.<br />
<br />
To allow using Slicer's Python interpreter in these IDEs, we had to add <tt>PythonSlicer</tt>, but kept <tt>SlicerPython</tt> around for not immediately breaking things.<br />
<br />
This redundancy is confusing for users that we could resolve by simply removing SlicerPython for Slicer5.<br />
<br />
<b>References:</b><br />
<br />
https://github.com/Slicer/Slicer/issues/4843<br />
<br />
===Slicer 5.0: Application must be installed in writable location to install extensions===<br />
<br />
Extensions are now installed in the application home folder because Python packages are installed there anyway, so the application home folder has to be writable (or Slicer has to be run as admin when you install extensions). It also allows making Slicer fully portable - see details [https://discourse.slicer.org/t/slicer-is-now-fully-portable/15410 here].<br />
<br />
If you want to allow any user to install extensions without admin rights and you only need to use extensions that don’t install Python packages at runtime then you can specify a custom extension install path folder in Slicer-NNN.ini file or revert to the old behavior of Slicer by specifying <code>Slicer_STORE_SETTINGS_IN_APPLICATION_HOME_DIR:BOOL=OFF</code> when configuring your Slicer build.<br />
<br />
===Slicer 5.0: Models are saved in LPS coordinate system by default===<br />
<br />
While Slicer uses RAS coordinate system internally, images, transforms, and markups files are stored in LPS coordinate system, because DICOM and all medical image computing software (maybe except a few very old ones) uses LPS coordinate system in files.<br />
<br />
However, Slicer has been still using its internal RAS coordinate system in mesh files (STL, VTK, VTP, OBJ, PLY), which caused issues when interfacing with third-party software.<br />
<br />
From Slicer-4.11.0-2020-02-26 (revision 28794) models are saved in LPS coordinate system, and mesh files assumed to be in LPS coordinate system by default (if no other coordinate system specified in the file).<br />
<br />
Slicer started embedding coordinate system name in mesh files a few years ago (see <code>SPACE=RAS</code> in the file header), so all the files that Slicer saved in recent years will load correctly and any scene files created with any version of Slicer will also load the models with correct orientation, too.<br />
<br />
Manual setting of coordinate system (in Add data dialog / Options column) is only needed when loading a mesh file without a scene that were created by Slicer-4.6 (2017-09-27) and earlier; and obj files created by Slicer-4.6 and Slicer-4.8 (between 2016-10-11 and 2018-03-26), or files are created by third-party software in RAS coordinate system.<br />
<br />
If you encounter orientation issues when loading a model file, you have the following options:<br />
<br />
*Option A: Specify the coordinate system when you open the model file. In “Add data” dialog, click “Show Options” and then choose “RAS” as coordinate system.<br />
*Option B: Update the third-party software that generate the mesh to save coordinates in LPS coordinate system instead of RAS coordinate system. Conversion is simple inverting the sign of the first two coordinates.<br />
*Option C: Write <code>SPACE=RAS</code> in the comment/description field in the mesh file (for STL, OBJ, PLY, VTK file; for VTP files, add in the first value of a vtkStringArray field array named <code>SPACE</code>) to indicate that the values are stored in RAS coordinate system. This option is useful if coordinates have to be stored in RAS coordinate system (for example, for compatibility with other software). See implementation example [https://github.com/Slicer/SlicerGitSVNArchive/blob/c0829f596f0ea661e0c5484056bd1374a3d22958/Libs/MRML/Core/vtkMRMLModelStorageNode.cxx#L421-L647 here].<br />
<br />
See more information, discussion of this topic on the [https://discourse.slicer.org/t/model-files-are-now-saved-in-lps-coordinate-system/10446 Slicer forum].<br />
<br />
===Slicer 5.0: CLI module descriptor XML files assume LPS coordinate system by default===<br />
<br />
If [[Documentation/Nightly/Developers/SlicerExecutionModel|SlicerExecutionModel]] descriptor XML file of a CLI module does not specify coordinate system for a point, pointfile, or region element then the coordinate system is assumed to be "lps". To preserve previous behavior and use "ras" coordinate system instead, add '''coordinateSystem="ras"''' to the element.<br />
<br />
===Slicer 5.0: Sequences extension has been merged into Slicer core===<br />
<br />
Sequences extension has been merged into Slicer core, therefore extensions do not need to depend on Sequences extension anymore.<br />
<br />
SequenceBrowser module has been merged into Sequences module, therefore previous code that used SequenceBrowser module now should use Sequences module instead.<br />
<br />
===Slicer 5.0: FreeSurfer support has been removed from Slicer core===<br />
The loading of FreeSurfer models and scalar overlays, as well as the FreeSurfer-specific color nodes, have been moved to the new [https://github.com/PerkLab/SlicerFreeSurfer SlicerFreeSurfer] extension. Tutorials on how to use the FreeSurfer Importer module to load multiple files at once can be found on the [https://github.com/PerkLab/SlicerFreeSurfer/wiki/Tutorials SlicerFreeSurfer tutorial page].<br />
<br />
===Slicer 5.0: Removed Editor module===<br />
<br />
The legacy Editor module has been deprecated since about 2017 and got removed in November 2021. It is replaced by the much improved Segment Editor module.<br />
<br />
===Slicer 5.0: Removed Charts and DoubleArrays module===<br />
<br />
Charts and DoubleArrays module have been deprecated since about 2018 and got removed in November 2021. They are replaced by Plots and Tables modules.<br />
<br />
'''Example of commits'''<br />
* [https://github.com/SlicerRt/SlicerRT/commit/8f9155f94399be71e747d3d45b1f6c4152caa139 SlicerRT@8f9155f94] ENH: Update DVH module to use plots infrastructure instead of charts<br />
<br />
===Slicer 5.0 : Avoid typedef of anonymous structure===<br />
<br />
Due to a recent (but retroactive) C++ rule change, only sufficiently C-compatible classes are permitted to be given a typedef name for linkage purposes. Add an <tt>enabled-by-default</tt> warning for these cases, and rephrase our existing error for the case where we encounter the <tt>typedef</tt> name for linkage after we've already computed and used a wrong linkage in terms of the new rule.<br />
<br />
<b>To fix warning message similar to:</b><br />
<br />
<pre><br />
Slicer/Libs/MRML/Core/vtkMRMLTableStorageNode.h:95:17:<br />
warning: anonymous non-C-compatible type given name for linkage purposes by typedef declaration; add a tag name here [-Wnon-c-typedef-for-linkage]<br />
typedef struct<br />
^<br />
ColumnInfo<br />
Slicer/Libs/MRML/Core/vtkMRMLTableStorageNode.h:99:5:<br />
note: type is not C-compatible due to this default member initializer<br />
int ScalarType = VTK_STRING;<br />
^~~~~~~~~~~~~~<br />
Slicer/Libs/MRML/Core/vtkMRMLTableStorageNode.h:102:5:<br />
note: type is given name 'ColumnInfo' for linkage purposes by this typedef declaration<br />
} ColumnInfo;<br />
^<br />
For consistency, Use 'using' to a named structure definintion for all <br />
structures.<br />
</pre><br />
<br />
<b>Replace code like this:</b><br />
<br />
<pre><br />
typedef struct<br />
{<br />
std::string ColumnName;<br />
std::vector<vtkAbstractArray*> RawComponentArrays;<br />
int ScalarType = VTK_STRING;<br />
std::vector<std::string> ComponentNames;<br />
std::string NullValueString;<br />
} ColumnInfo;<br />
</pre><br />
<br />
<b>By this:</b><br />
<br />
<pre><br />
struct StructColumnInfo<br />
{<br />
std::string ColumnName;<br />
std::vector<vtkAbstractArray*> RawComponentArrays;<br />
int ScalarType = VTK_STRING;<br />
std::vector<std::string> ComponentNames;<br />
std::string NullValueString;<br />
};<br />
using ColumnInfo = struct StructColumnInfo;<br />
</pre><br />
<br />
<br />
<b>References:</b><br />
<br />
https://reviews.llvm.org/D74103<br />
<br />
===Slicer 5.0 : Temporary path===<br />
<br />
Temporary path was stored redundantly application settings (Slicer.ini) in two keys: <code>Modules/TemporaryDirectory</code> and <code>TemporaryPath</code>. <code>Modules/TemporaryDirectory</code> overwrote <code>TemporaryPath</code> at startup, but when temporary path was set via the <code>slicer.app.temporaryPath</code> then it was only written to <code>TemporaryPath</code>.<br />
<br />
Changed behavior so that only <code>TemporaryPath</code> is used. <code>Modules/TemporaryDirectory</code> is ignored.<br />
<br />
To ensure that temporary path is always writable, it as checked at startup that a file can be created in temporary path and if this check fails then temporary path is reset to default (<code>QDir::tempPath()</code>).<br />
<br />
===Slicer 5.0 : Always prefer executable CLIs===<br />
<br />
Previously, if a CLI module was available both as an executable and a shared library, then PreferExecutableCLI application setting was used to determine which one is used. Now always CLIs are always executed in an external process (if an executable is available). Reasons are described in this issue: https://github.com/Slicer/Slicer/issues/4893. The application setting is no more displayed in the GUI and any setting specified in earlier Slicer versions is ignored.<br />
<br />
===Slicer 5.0 : SlicerApp-real is a console application on Windows===<br />
<br />
Previously, the application (SlicerApp-real.exe) was built as a GUI application (without console) on Windows, to avoid displaying a terminal window when starting the application. This had the drawback that the Slicer application did not have standard input/output that could be displayed or redirected (for example, for capturing into a file). SlicerApp-real has always been a console application on Linux and macOS, therefore this change makes the software behavior more consistent across platforms.<br />
<br />
SlicerApp-real.exe is now built as a console application (see [https://github.com/Slicer/Slicer/issues/2934 #2934]). Displaying of a terminal window is prevented by using a launcher (Slicer.exe) that is built as a GUI application and it starts Slicer with the standard input and outputs redirected.<br />
<br />
To display console output: https://slicer.readthedocs.io/en/latest/developer_guide/debugging/overview.html#console-output-on-windows<br />
<br />
To launch a command-line terminal using <code>subprocess.Popen</code> that shows a new terminal, specify <code>creationflags=subprocess.CREATE_NEW_CONSOLE</code> argument.<br />
<br />
===Slicer 4.11: Variable CMAKE_DEFAULT_BUILD_TYPE renamed to Slicer_DEFAULT_BUILD_TYPE===<br />
<br />
Setting the default build type for single config generator may be done setting <tt>Slicer_DEFAULT_BUILD_TYPE</tt> instead of <tt>CMAKE_DEFAULT_BUILD_TYPE</tt>.<br />
<br />
<b>Error message similar to:</b><br />
<br />
<pre><br />
CMake Error:<br />
Generator<br />
<br />
Visual Studio 15 2017<br />
<br />
does not support variable<br />
<br />
CMAKE_DEFAULT_BUILD_TYPE<br />
<br />
but it has been specified.<br />
</pre><br />
<br />
<b>References:</b><br />
<br />
https://github.com/Slicer/Slicer/pull/4799<br />
<br />
===Slicer 4.11: teem python module renamed to vtkTeem, explicit import required===<br />
<br />
*Since the module provides VTK classes interfacing with "teem", the name is now representative of the class it contains.<br />
*<tt>vtkTeem</tt> classes are expected to be used by explicitly importing the module.<br />
<br />
<b>Replace code like this:</b><br />
<br />
<pre><br />
import teem<br />
<br />
class CalculateTensorScalars(object):<br />
def __init__(self):<br />
self.dti_math = teem.vtkDiffusionTensorMathematics()<br />
</pre><br />
<br />
<b>By this:</b><br />
<br />
<pre><br />
import vtkTeem<br />
<br />
class CalculateTensorScalars(object):<br />
def __init__(self):<br />
self.dti_math = vtkTeem.vtkDiffusionTensorMathematics()<br />
</pre><br />
<br />
===Slicer 4.11: Display window/level (brightness/contrast) adjustment===<br />
<br />
*A new "Window/level" mouse interaction mode was introduced. Volume display window/level can only be changed if this mode is activated by clicking the corresponding button in the toolbar. The new mouse mode prevents accidental modification of volume window/level (when for example the user accidentally clicked too far from a markup) and it also allows more sophisticated window/level adjustments.<br />
*New region-based auto window/level feature added: activate "Window/level" mouse mode and use Ctrl + left-click-and-drag to highlight a region and optimize window/level for that (pressing Escape or right-click cancels the operation).<br />
*Auto window/level reset: activate "Window/level" mouse mode and double-click the left mouse button.<br />
*Improved auto window/level algorithm to prevent too bright display of images. Window/level is set to display values between 0.1th and 99.9th percentile of gray levels. See details here: https://discourse.slicer.org/t/feedback-requested-how-to-improve-mouse-interaction-in-views/6420.<br />
*Removed class <tt>vtkImageBimodalAnalysis</tt><br />
<br />
===Slicer 4.10: Registration of runTest function done in ScriptedLoadableModule base class===<br />
<br />
Following [http://viewvc.slicer.org/viewvc.cgi/Slicer4?view=revision&revision=27617 r27617]:<br />
<br />
*the <code>ScriptedLoadableModule</code> class takes care of registering the <code>runTest</code> function.<br />
*the <code>runTest</code> function expects <code>msec</code> keyword argument.<br />
<br />
<b>Error message similar to:</b><br />
<br />
<pre><br />
Traceback (most recent call last):<br />
File "/path/to/Slicer-SuperBuild/Slicer-build/bin/Python/slicer/ScriptedLoadableModule.py", line 205, in onReloadAndTest<br />
test(msec=int(slicer.app.userSettings().value("Developer/SelfTestDisplayMessageDelay")), **kwargs)<br />
TypeError: runTest() got an unexpected keyword argument 'msec'<br />
Reload and Test: Exception!<br />
<br />
runTest() got an unexpected keyword argument 'msec'<br />
</pre><br />
<br />
<b>Replace code like this:</b><br />
<br />
<pre><br />
class sceneImport2428(ScriptedLoadableModule):<br />
[...]<br />
def __init__(self, parent):<br />
ScriptedLoadableModule.__init__(self, parent)<br />
parent.title = "..."<br />
[...]<br />
parent.acknowledgementText = "..."<br />
self.parent = parent <br />
<br />
# Add this test to the SelfTest module's list for discovery when the module <br />
# is created. Since this module may be discovered before SelfTests itself, <br />
# create the list if it doesn't already exist. <br />
try: <br />
slicer.selfTests <br />
except AttributeError: <br />
slicer.selfTests = {} <br />
slicer.selfTests['sceneImport2428'] = self.runTest <br />
<br />
def runTest(self): <br />
tester = sceneImport2428Test() <br />
tester.runTest()<br />
<br />
[...]<br />
</pre><br />
<br />
<b>By this:</b><br />
<br />
<pre><br />
class sceneImport2428(ScriptedLoadableModule):<br />
[...]<br />
def __init__(self, parent):<br />
ScriptedLoadableModule.__init__(self, parent)<br />
parent.title = "..."<br />
[...]<br />
parent.acknowledgementText = "..."<br />
<br />
[...]<br />
</pre><br />
<br />
===Slicer 4.9: Update of VTK version from 9.0 to 8.2===<br />
<br />
Following [https://github.com/Kitware/VTK/commit/b703d78be3ffd8ae69c319afa0230097ff270f26 kitware/VTK@b703d78be], VTK has updated to use version number 8.2 instead of 9.0. This was discussed in on the VTK mailing list in http://vtk.1045678.n5.nabble.com/Discussion-OK-to-change-VTK-s-version-number-from-9-0-to-8-2-tt5748702.html<br />
<br />
At first, this VTK commit and its companion [https://github.com/Kitware/VTK/commit/8a00b357e84eec695bda049216f30f2b76d80855 kitware/VTK@8a00b357e] were both reverted from the [https://github.com/Slicer/VTK/ Slicer/VTK] fork. Then, since having the corresponding changes reverted in VTK was not possible, it was decided to also update Slicer. This was done in the following commits:<br />
<br />
*[http://viewvc.slicer.org/viewvc.cgi/Slicer4?view=revision&revision=27472 r27472]: COMP: Update c++ classes to support building against VTK >= 9 and VTK >= 8.2<br />
*[http://viewvc.slicer.org/viewvc.cgi/Slicer4?view=revision&revision=27473 r27473]: COMP: Update VTK to include version change from 9.0 to 8.2. Fixes #4623<br />
<br />
This means that code depending on VTK must also be updated to include similar fixes.<br />
<br />
'''Replace this:'''<br />
#if VTK_MAJOR_VERSION >= 9<br />
<br />
'''By this:'''<br />
#if VTK_MAJOR_VERSION >= 9 || (VTK_MAJOR_VERSION >= 8 && VTK_MINOR_VERSION >= 2)<br />
<br />
and<br />
<br />
'''Replace this:'''<br />
#if VTK_MAJOR_VERSION < 9<br />
<br />
'''By this:'''<br />
#if VTK_MAJOR_VERSION <= 7 || (VTK_MAJOR_VERSION <= 8 && VTK_MINOR_VERSION <= 1)<br />
<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 />
<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 />
<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 />
===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 />
<br />
*https://www.slicer.org/wiki/Documentation/Labs/Segmentations#vtkMRMLLabelMapVolumeNode_integration* 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>Jamesobutlerhttps://www.slicer.org/w/index.php?title=Documentation/Nightly/Developers/Tutorials/MigrationGuide/Slicer&diff=63578Documentation/Nightly/Developers/Tutorials/MigrationGuide/Slicer2021-05-04T01:58:01Z<p>Jamesobutler: add supporting only python 3.6 and above</p>
<hr />
<div><noinclude>__TOC__</noinclude><br />
==Slicer backward incompatible changes==<br />
<br />
===Slicer 5.0: API changes since 4.10===<br />
<br />
*Removed protected method <tt>vtkMRMLModelDisplayableManager::FindPickedDisplayNodeFromMesh</tt><br />
<br />
==== Supporting only Python 3.6 and above ====<br />
Slicer python code has been updated to support Python 3.6 and above syntax using [https://github.com/asottile/pyupgrade pyupgrade] to automatically update the syntax.<br />
<br />
Install pyupgrade: <code>PythonSlicer -m pip install pyupgrade</code><br />
<br />
* Running:<br />
** On 1 file: <code>PythonSlicer -m pyupgrade --py36-plus MyPythonFile.py</code><br />
** On multiple files: Here is my pyupgrade-script.py written to automate running pyupgrade across all python files in the Slicer repo. It was run by <code>PythonSlicer pyupgrade-script.py</code><br />
<syntaxhighlight lang="python"><br />
# pyupgrade-script.py<br />
import os<br />
import subprocess<br />
<br />
search_directory = "C:/Users/MyUserName/Documents/GitHub/Slicer"<br />
for root, _, files in os.walk(search_directory):<br />
for file_item in files:<br />
file_path = os.path.join(root, file_item)<br />
if os.path.isfile(file_path) and file_path.endswith(".py"):<br />
subprocess.call(["PythonSlicer", "-m", "pyupgrade", "--py36-plus", file_path])<br />
</syntaxhighlight><br />
<br />
====Python 2 to Python 3====<br />
<br />
Slicer core has been updated to only support Python 3.<br />
<br />
C++ classes and python scripts have been updated to use idioms and constructs only available in Python 3.<br />
<br />
Update to python scripts have been done leveraging the CLI provided by https://python-future.org by (1) iteratively applying each one of the associates "fixes", (2) reviewing associated changes and (3) updating as needed.<br />
<br />
Updates specific to extensions are discussed in [[Documentation/Nightly/Developers/Tutorials/MigrationGuide#Slicer_5.0:_Python2_to_Python3]]<br />
<br />
====Interactor styles====<br />
<br />
Limitations of VTK widgets (editable points, lines, curves, etc.) prevented Slicer from having sophisticated user interaction in slice and 3D views. In Slicer5, we replaced VTK widgets with MRML widgets. These widgets are still VTK-based and somewhat similar to VTK widgets, but they operate directly on MRML nodes, they use direct method calls between widgets and their representation, and they use a more efficient and flexible event processing. Instead of hardcoding how viewers behave in response to interaction (mouse move, button click, keyboard, ...) events in an interactor style, all these events are translated to actions and performed in a MRML widget. Most modules are not expected to observe interactor events or styles directly, but if they did, then they may need to be updated accordingly.<br />
<br />
*vtkSliceViewInteractorStyle renamed to vtkMRMLSliceDViewInteractorStyle to reflect that it uses MRML classes directly.<br />
*vtkThreeDViewInteractorStyle renamed to vtkMRMLThreeDViewInteractorStyle to reflect that it uses MRML classes directly.<br />
<br />
====slicer.util functions====<br />
<br />
*slicer.util.loadVolume (and other node load functions) now return the loaded node instead of a True/False flag. In case of an error, a RuntimeError exception is thrown.<br />
**Old way of loading a node and get it in a variable: <code>volumeNode = slicer.util.loadVolume('path/to/volume.nrrd', returnNode=True)[1]</code><br />
**New way of loading a node and get it in a variable: <code>volumeNode = slicer.util.loadVolume('path/to/volume.nrrd')</code><br />
<br />
====Markups====<br />
<br />
*<tt>vtkCommand::Modified</tt> events are no longer invoked when control points are added/removed/modified to improve performance. Modules that need to know If a point position is modified need to add observers to <tt>vtkMRMLMarkupsNode::PointAddedEvent</tt>, <tt>vtkMRMLMarkupsNode::PointRemovedEvent</tt>, <tt>vtkMRMLMarkupsNode::PointModifiedEvent</tt> events. See example in [https://slicer.readthedocs.io/en/latest/developer_guide/script_repository.html#get-a-notification-if-a-markup-point-position-is-modified Script repository].<br />
*<tt>vtkMRMLMarkupsNode::MarkupAddedEvent</tt> is renamed to <tt>PointPositionDefinedEvent</tt>. There is a similar event, <tt>vtkMRMLMarkupsNode::PointAddedEvent</tt>, which is called even when preview point is created.<br />
*<tt>vtkMRMLMarkupsNode::MarkupRemovedEvent</tt> is renamed to <tt>vtkMRMLMarkupsNode::PointPositionUndefinedEvent</tt>. There is a similar event, <tt>vtkMRMLMarkupsNode::PointRemovedEvent</tt>, which is called even when preview point is removed.<br />
*<tt>vtkMRMLMarkupsNode::NthMarkupModifiedEvent</tt> is replaced by <tt>vtkMRMLMarkupsNode::PointModifiedEvent</tt><br />
*During placement of markups, a preview markup point is created. If number of already placed markup points needs to be determined then <code>GetNumberOfDefinedControlPoints()</code> method can be used.<br />
*<tt>GetDefaultMarkups...()</tt> and <tt>SetDefaultMarkups...()</tt> methods are removed. Instead default display node can be accessed by <tt>GetDefaultMarkupsDisplayNode()</tt> method and default values can be get/set in that class.<br />
*<tt>vtkMRMLMarkupsNode::GetNthMarkupSelected()</tt> is replaced by <tt>GetNthControlPointSelected()</tt><br />
*<tt>vtkMRMLMarkupsNode::PointPositionDefinedEvent</tt> event is added. This event is invoked whenever position is defined for a new point.<br />
*<tt>vtkMRMLMarkupsNode::PointPositionUndefinedEvent</tt> event is added. This event is invoked whenever point with defined position is removed (point is deleted or its position gets undefined).<br />
*For more details, see [{{doxygen-class-url|vtkMRMLMarkupsNode}} vtkMRMLMarkupsNode]<br />
<br />
====Segmentations====<br />
<br />
Binary labelmap segmentations can now be represented as shared labelmaps.<br />
The previous implementation of binary labelmaps was performance intensive as each labelmap was represented using a separate vtkDataObject.<br />
Visualizing and editing segmentations that contained a large number of segments could cause performance issues, due to the large number of vtkActors required, as well as calculating masks and overwriting other segments when editing.<br />
<br />
By default, newly created segments will now be contained on the same layer.<br />
Segments will only be separated into multiple layers if the user creates an overlapping segment when editing.<br />
<br />
Segments are now saved as a 4D volume with shared 3D layers.<br />
For a segmentation that only uses one layer, the resulting image is a 3D volume.<br />
Before saving, the labelmaps will be collapsed into as few layers as possible.<br />
<br />
*seg.nrrd files now contain two additional attributes for each segment: SegmentX_LabelValue and SegmentX_Layer<br />
*The label value of a segment can be found using vtkSegment::GetLabelValue()<br />
*Whether or not a segment is shared can be found using vtkSegmentation::IsSharedBinaryLabelmap()<br />
*The other segments sharing the same labelmap can be found using vtkSegmentation::GetSegmentIDsSharingBinaryLabelmapRepresentation()<br />
*Segment editor effects should generally use modifySelectedSegmentByLabelmap rather than SetBinaryLabelmapToSegment to manage layer separation<br />
*Conversion rules now call PreConvert() and PostConvert() before and after conversion to perform pre and post processing steps on the segmentation as a whole<br />
*The function signature for vtkSegmentationConverterRule::Convert now accepts a vtkSegment rather than two vtkDataObjects<br />
*slicer.util.arrayFromSegment has been deprecated. slicer.util.arrayFromSegmentBinaryLabelmap and slicer.util.arrayFromSegmentInternalBinaryLabelmap can be used instead<br />
<br />
=====Erase the contents of a single segment=====<br />
<br />
<pre><br />
segmentation = segmentationNode.GetSegmentation()<br />
segmentation.ClearSegment(segmentId)<br />
</pre><br />
<br />
=====Set labelmap in a segment=====<br />
<br />
Directly, bypassing masking settings:<br />
<br />
<pre><br />
slicer.vtkSlicerSegmentationsModuleLogic.SetBinaryLabelmapToSegment(orientedImageDataToSet, segmentationNode, segmentId)<br />
</pre><br />
<br />
=====Move a segment from a shared labelmap to a separate layer=====<br />
<br />
<pre><br />
segmentation = segmentationNode.GetSegmentation()<br />
segmentation.SeparateSegmentLabelmap(segmentId)<br />
</pre><br />
<br />
=====Combine all binary labelmaps to as few layers as possible=====<br />
<br />
<pre><br />
segmentation = segmentationNode.GetSegmentation()<br />
segmentation.CollapseBinaryLabelmaps(forceToSingleLayer=false)<br />
</pre><br />
<br />
<b>Get a read-only labelmap for a single segment:</b><br />
<br />
<pre><br />
labelmap = slicer.vtkOrientedImageData()<br />
segmentationNode.GetBinaryLabelmapRepresentation(segmentId, labelmap)<br />
</pre><br />
<br />
(similarly, use GetClosedSurfaceRepresentation with an additional vtk.vtkPolyData parameter to get a read-only surface mesh)<br />
<br />
or<br />
<br />
<pre><br />
labelmapNumpyArray = slicer.util.arrayFromSegmentBinaryLabelmap(segmentationNode, segmentId)<br />
</pre><br />
<br />
=====Get a modifiable shared labelmap=====<br />
<br />
<pre><br />
labelmap = slicer.vtkOrientedImageData()<br />
segmentationNode.GetBinaryLabelmapInternalRepresentation(segmentId, labelmap)<br />
</pre><br />
<br />
(similarly, use GetClosedSurfaceInternalRepresentation to get a modifiable surface mesh)<br />
<br />
or<br />
<br />
<pre><br />
labelmapNumpyArray = slicer.util.arrayFromSegmentInternalBinaryLabelmap(segmentationNode, segmentId)<br />
</pre><br />
<br />
=====Export segments to models=====<br />
<br />
Model hierarchies no longer exist in Slicer5, but instead various kinds of hierarchies are now replaced by "subject hierarchy", which can accommodate any node types in a single hierarchy. Accordingly, `ExportSegmentsToModelHierarchy`, `ExportAllSegmentsToModelHierarchy`, etc. are replaced by `ExportSegmentsToModels`, `ExportAllSegmentsToModels`, which take a subject hierarchy folder item ID as input.<br />
Documentation/Nightly<br />
See code example in [https://slicer.readthedocs.io/en/latest/developer_guide/script_repository.html#export-model-nodes-from-segmentation-node Script repository].<br />
<br />
====Volume rendering====<br />
<br />
vtkMRMLVolumeRenderingDisplayNode::SetAndObserveVolumeNodeID method was removed, as display node base class already maintains a pointer to the displayed (volume) node. To associate a volume display node with a volume node, call <pre>volumeNode->AddAndObserveDisplayNodeID(volumeRenderingDisplayNode->GetID());</pre> after both nodes are added to the scene.<br />
<br />
===Slicer 5.0: SlicerPython was removed. Use PythonSlicer instead===<br />
<br />
<b>Error message:</b><br />
<br />
<pre><br />
SlicerPython executable is obsolete and will be removed. Use PythonSlicer executable instead.<br />
For more details, see https://github.com/Slicer/Slicer/issues/4843<br />
</pre><br />
<br />
<b>Solution:</b><br />
<br />
Use <tt>PythonSlicer</tt> instead of <tt>SlicerPython</tt><br />
<br />
<b>Background:</b><br />
<br />
Python IDEs (specifically PyCharm, but potentially others) only recognize <tt>Python*.exe</tt> files as Python interpreters.<br />
<br />
To allow using Slicer's Python interpreter in these IDEs, we had to add <tt>PythonSlicer</tt>, but kept <tt>SlicerPython</tt> around for not immediately breaking things.<br />
<br />
This redundancy is confusing for users that we could resolve by simply removing SlicerPython for Slicer5.<br />
<br />
<b>References:</b><br />
<br />
https://github.com/Slicer/Slicer/issues/4843<br />
<br />
===Slicer 5.0: Application must be installed in writable location to install extensions===<br />
<br />
Extensions are now installed in the application home folder because Python packages are installed there anyway, so the application home folder has to be writable (or Slicer has to be run as admin when you install extensions). It also allows making Slicer fully portable - see details [https://discourse.slicer.org/t/slicer-is-now-fully-portable/15410 here].<br />
<br />
If you want to allow any user to install extensions without admin rights and you only need to use extensions that don’t install Python packages at runtime then you can specify a custom extension install path folder in Slicer-NNN.ini file or revert to the old behavior of Slicer by specifying <code>Slicer_STORE_SETTINGS_IN_APPLICATION_HOME_DIR:BOOL=OFF</code> when configuring your Slicer build.<br />
<br />
===Slicer 5.0: Models are saved in LPS coordinate system by default===<br />
<br />
While Slicer uses RAS coordinate system internally, images, transforms, and markups files are stored in LPS coordinate system, because DICOM and all medical image computing software (maybe except a few very old ones) uses LPS coordinate system in files.<br />
<br />
However, Slicer has been still using its internal RAS coordinate system in mesh files (STL, VTK, VTP, OBJ, PLY), which caused issues when interfacing with third-party software.<br />
<br />
From Slicer-4.11.0-2020-02-26 (revision 28794) models are saved in LPS coordinate system, and mesh files assumed to be in LPS coordinate system by default (if no other coordinate system specified in the file).<br />
<br />
Slicer started embedding coordinate system name in mesh files a few years ago (see <code>SPACE=RAS</code> in the file header), so all the files that Slicer saved in recent years will load correctly and any scene files created with any version of Slicer will also load the models with correct orientation, too.<br />
<br />
Manual setting of coordinate system (in Add data dialog / Options column) is only needed when loading a mesh file without a scene that were created by Slicer-4.6 (2017-09-27) and earlier; and obj files created by Slicer-4.6 and Slicer-4.8 (between 2016-10-11 and 2018-03-26), or files are created by third-party software in RAS coordinate system.<br />
<br />
If you encounter orientation issues when loading a model file, you have the following options:<br />
<br />
*Option A: Specify the coordinate system when you open the model file. In “Add data” dialog, click “Show Options” and then choose “RAS” as coordinate system.<br />
*Option B: Update the third-party software that generate the mesh to save coordinates in LPS coordinate system instead of RAS coordinate system. Conversion is simple inverting the sign of the first two coordinates.<br />
*Option C: Write <code>SPACE=RAS</code> in the comment/description field in the mesh file (for STL, OBJ, PLY, VTK file; for VTP files, add in the first value of a vtkStringArray field array named <code>SPACE</code>) to indicate that the values are stored in RAS coordinate system. This option is useful if coordinates have to be stored in RAS coordinate system (for example, for compatibility with other software). See implementation example [https://github.com/Slicer/SlicerGitSVNArchive/blob/c0829f596f0ea661e0c5484056bd1374a3d22958/Libs/MRML/Core/vtkMRMLModelStorageNode.cxx#L421-L647 here].<br />
<br />
See more information, discussion of this topic on the [https://discourse.slicer.org/t/model-files-are-now-saved-in-lps-coordinate-system/10446 Slicer forum].<br />
<br />
===Slicer 5.0: CLI module descriptor XML files assume LPS coordinate system by default===<br />
<br />
If [[Documentation/Nightly/Developers/SlicerExecutionModel|SlicerExecutionModel]] descriptor XML file of a CLI module does not specify coordinate system for a point, pointfile, or region element then the coordinate system is assumed to be "lps". To preserve previous behavior and use "ras" coordinate system instead, add '''coordinateSystem="ras"''' to the element.<br />
<br />
===Slicer 5.0: Sequences extension has been merged into Slicer core===<br />
<br />
Sequences extension has been merged into Slicer core, therefore extensions do not need to depend on Sequences extension anymore.<br />
<br />
SequenceBrowser module has been merged into Sequences module, therefore previous code that used SequenceBrowser module now should use Sequences module instead.<br />
<br />
===Slicer 5.0: FreeSurfer support has been removed from Slicer core===<br />
The loading of FreeSurfer models and scalar overlays, as well as the FreeSurfer-specific color nodes, have been moved to the new [https://github.com/PerkLab/SlicerFreeSurfer SlicerFreeSurfer] extension. Tutorials on how to use the FreeSurfer Importer module to load multiple files at once can be found on the [https://github.com/PerkLab/SlicerFreeSurfer/wiki/Tutorials SlicerFreeSurfer tutorial page].<br />
<br />
===Slicer 5.0 : Avoid typedef of anonymous structure===<br />
<br />
Due to a recent (but retroactive) C++ rule change, only sufficiently C-compatible classes are permitted to be given a typedef name for linkage purposes. Add an <tt>enabled-by-default</tt> warning for these cases, and rephrase our existing error for the case where we encounter the <tt>typedef</tt> name for linkage after we've already computed and used a wrong linkage in terms of the new rule.<br />
<br />
<b>To fix warning message similar to:</b><br />
<br />
<pre><br />
Slicer/Libs/MRML/Core/vtkMRMLTableStorageNode.h:95:17:<br />
warning: anonymous non-C-compatible type given name for linkage purposes by typedef declaration; add a tag name here [-Wnon-c-typedef-for-linkage]<br />
typedef struct<br />
^<br />
ColumnInfo<br />
Slicer/Libs/MRML/Core/vtkMRMLTableStorageNode.h:99:5:<br />
note: type is not C-compatible due to this default member initializer<br />
int ScalarType = VTK_STRING;<br />
^~~~~~~~~~~~~~<br />
Slicer/Libs/MRML/Core/vtkMRMLTableStorageNode.h:102:5:<br />
note: type is given name 'ColumnInfo' for linkage purposes by this typedef declaration<br />
} ColumnInfo;<br />
^<br />
For consistency, Use 'using' to a named structure definintion for all <br />
structures.<br />
</pre><br />
<br />
<b>Replace code like this:</b><br />
<br />
<pre><br />
typedef struct<br />
{<br />
std::string ColumnName;<br />
std::vector<vtkAbstractArray*> RawComponentArrays;<br />
int ScalarType = VTK_STRING;<br />
std::vector<std::string> ComponentNames;<br />
std::string NullValueString;<br />
} ColumnInfo;<br />
</pre><br />
<br />
<b>By this:</b><br />
<br />
<pre><br />
struct StructColumnInfo<br />
{<br />
std::string ColumnName;<br />
std::vector<vtkAbstractArray*> RawComponentArrays;<br />
int ScalarType = VTK_STRING;<br />
std::vector<std::string> ComponentNames;<br />
std::string NullValueString;<br />
};<br />
using ColumnInfo = struct StructColumnInfo;<br />
</pre><br />
<br />
<br />
<b>References:</b><br />
<br />
https://reviews.llvm.org/D74103<br />
<br />
===Slicer 5.0 : Temporary path===<br />
<br />
Temporary path was stored redundantly application settings (Slicer.ini) in two keys: <code>Modules/TemporaryDirectory</code> and <code>TemporaryPath</code>. <code>Modules/TemporaryDirectory</code> overwrote <code>TemporaryPath</code> at startup, but when temporary path was set via the <code>slicer.app.temporaryPath</code> then it was only written to <code>TemporaryPath</code>.<br />
<br />
Changed behavior so that only <code>TemporaryPath</code> is used. <code>Modules/TemporaryDirectory</code> is ignored.<br />
<br />
To ensure that temporary path is always writable, it as checked at startup that a file can be created in temporary path and if this check fails then temporary path is reset to default (<code>QDir::tempPath()</code>).<br />
<br />
===Slicer 4.11: Variable CMAKE_DEFAULT_BUILD_TYPE renamed to Slicer_DEFAULT_BUILD_TYPE===<br />
<br />
Setting the default build type for single config generator may be done setting <tt>Slicer_DEFAULT_BUILD_TYPE</tt> instead of <tt>CMAKE_DEFAULT_BUILD_TYPE</tt>.<br />
<br />
<b>Error message similar to:</b><br />
<br />
<pre><br />
CMake Error:<br />
Generator<br />
<br />
Visual Studio 15 2017<br />
<br />
does not support variable<br />
<br />
CMAKE_DEFAULT_BUILD_TYPE<br />
<br />
but it has been specified.<br />
</pre><br />
<br />
<b>References:</b><br />
<br />
https://github.com/Slicer/Slicer/pull/4799<br />
<br />
===Slicer 4.11: teem python module renamed to vtkTeem, explicit import required===<br />
<br />
*Since the module provides VTK classes interfacing with "teem", the name is now representative of the class it contains.<br />
*<tt>vtkTeem</tt> classes are expected to be used by explicitly importing the module.<br />
<br />
<b>Replace code like this:</b><br />
<br />
<pre><br />
import teem<br />
<br />
class CalculateTensorScalars(object):<br />
def __init__(self):<br />
self.dti_math = teem.vtkDiffusionTensorMathematics()<br />
</pre><br />
<br />
<b>By this:</b><br />
<br />
<pre><br />
import vtkTeem<br />
<br />
class CalculateTensorScalars(object):<br />
def __init__(self):<br />
self.dti_math = vtkTeem.vtkDiffusionTensorMathematics()<br />
</pre><br />
<br />
===Slicer 4.11: Display window/level (brightness/contrast) adjustment===<br />
<br />
*A new "Window/level" mouse interaction mode was introduced. Volume display window/level can only be changed if this mode is activated by clicking the corresponding button in the toolbar. The new mouse mode prevents accidental modification of volume window/level (when for example the user accidentally clicked too far from a markup) and it also allows more sophisticated window/level adjustments.<br />
*New region-based auto window/level feature added: activate "Window/level" mouse mode and use Ctrl + left-click-and-drag to highlight a region and optimize window/level for that (pressing Escape or right-click cancels the operation).<br />
*Auto window/level reset: activate "Window/level" mouse mode and double-click the left mouse button.<br />
*Improved auto window/level algorithm to prevent too bright display of images. Window/level is set to display values between 0.1th and 99.9th percentile of gray levels. See details here: https://discourse.slicer.org/t/feedback-requested-how-to-improve-mouse-interaction-in-views/6420.<br />
*Removed class <tt>vtkImageBimodalAnalysis</tt><br />
<br />
===Slicer 4.10: Registration of runTest function done in ScriptedLoadableModule base class===<br />
<br />
Following [http://viewvc.slicer.org/viewvc.cgi/Slicer4?view=revision&revision=27617 r27617]:<br />
<br />
*the <code>ScriptedLoadableModule</code> class takes care of registering the <code>runTest</code> function.<br />
*the <code>runTest</code> function expects <code>msec</code> keyword argument.<br />
<br />
<b>Error message similar to:</b><br />
<br />
<pre><br />
Traceback (most recent call last):<br />
File "/path/to/Slicer-SuperBuild/Slicer-build/bin/Python/slicer/ScriptedLoadableModule.py", line 205, in onReloadAndTest<br />
test(msec=int(slicer.app.userSettings().value("Developer/SelfTestDisplayMessageDelay")), **kwargs)<br />
TypeError: runTest() got an unexpected keyword argument 'msec'<br />
Reload and Test: Exception!<br />
<br />
runTest() got an unexpected keyword argument 'msec'<br />
</pre><br />
<br />
<b>Replace code like this:</b><br />
<br />
<pre><br />
class sceneImport2428(ScriptedLoadableModule):<br />
[...]<br />
def __init__(self, parent):<br />
ScriptedLoadableModule.__init__(self, parent)<br />
parent.title = "..."<br />
[...]<br />
parent.acknowledgementText = "..."<br />
self.parent = parent <br />
<br />
# Add this test to the SelfTest module's list for discovery when the module <br />
# is created. Since this module may be discovered before SelfTests itself, <br />
# create the list if it doesn't already exist. <br />
try: <br />
slicer.selfTests <br />
except AttributeError: <br />
slicer.selfTests = {} <br />
slicer.selfTests['sceneImport2428'] = self.runTest <br />
<br />
def runTest(self): <br />
tester = sceneImport2428Test() <br />
tester.runTest()<br />
<br />
[...]<br />
</pre><br />
<br />
<b>By this:</b><br />
<br />
<pre><br />
class sceneImport2428(ScriptedLoadableModule):<br />
[...]<br />
def __init__(self, parent):<br />
ScriptedLoadableModule.__init__(self, parent)<br />
parent.title = "..."<br />
[...]<br />
parent.acknowledgementText = "..."<br />
<br />
[...]<br />
</pre><br />
<br />
===Slicer 4.9: Update of VTK version from 9.0 to 8.2===<br />
<br />
Following [https://github.com/Kitware/VTK/commit/b703d78be3ffd8ae69c319afa0230097ff270f26 kitware/VTK@b703d78be], VTK has updated to use version number 8.2 instead of 9.0. This was discussed in on the VTK mailing list in http://vtk.1045678.n5.nabble.com/Discussion-OK-to-change-VTK-s-version-number-from-9-0-to-8-2-tt5748702.html<br />
<br />
At first, this VTK commit and its companion [https://github.com/Kitware/VTK/commit/8a00b357e84eec695bda049216f30f2b76d80855 kitware/VTK@8a00b357e] were both reverted from the [https://github.com/Slicer/VTK/ Slicer/VTK] fork. Then, since having the corresponding changes reverted in VTK was not possible, it was decided to also update Slicer. This was done in the following commits:<br />
<br />
*[http://viewvc.slicer.org/viewvc.cgi/Slicer4?view=revision&revision=27472 r27472]: COMP: Update c++ classes to support building against VTK >= 9 and VTK >= 8.2<br />
*[http://viewvc.slicer.org/viewvc.cgi/Slicer4?view=revision&revision=27473 r27473]: COMP: Update VTK to include version change from 9.0 to 8.2. Fixes #4623<br />
<br />
This means that code depending on VTK must also be updated to include similar fixes.<br />
<br />
'''Replace this:'''<br />
#if VTK_MAJOR_VERSION >= 9<br />
<br />
'''By this:'''<br />
#if VTK_MAJOR_VERSION >= 9 || (VTK_MAJOR_VERSION >= 8 && VTK_MINOR_VERSION >= 2)<br />
<br />
and<br />
<br />
'''Replace this:'''<br />
#if VTK_MAJOR_VERSION < 9<br />
<br />
'''By this:'''<br />
#if VTK_MAJOR_VERSION <= 7 || (VTK_MAJOR_VERSION <= 8 && VTK_MINOR_VERSION <= 1)<br />
<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 />
<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 />
<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 />
===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 />
<br />
*https://www.slicer.org/wiki/Documentation/Labs/Segmentations#vtkMRMLLabelMapVolumeNode_integration* 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>Jamesobutlerhttps://www.slicer.org/w/index.php?title=Documentation/Nightly/Developers/Tutorials/MigrationGuide/SlicerExtension&diff=61513Documentation/Nightly/Developers/Tutorials/MigrationGuide/SlicerExtension2019-11-05T23:17:28Z<p>Jamesobutler: Added dicom to pydicom migration note</p>
<hr />
<div><noinclude>__TOC__</noinclude><br />
==Slicer Extension updates==<br />
<br />
===Slicer 5.0: ITKv4 to ITKv5===<br />
<br />
To remove support for ITKv4 and only support ITKv5. See [[Documentation/Nightly/Developers/Tutorials/MigrationGuide#Transition_from_ITK4_to_ITK5]], it includes<br />
the following sections:<br />
* [[Documentation/Nightly/Developers/Tutorials/MigrationGuide#Upgrading_to_ITKv5_or_keep_using_ITKv4_GenerateThreadedData|Upgrading to ITKv5 or keep using ITKv4 GenerateThreadedData]]<br />
* [[Documentation/Nightly/Developers/Tutorials/MigrationGuide#itkMultiThreader_refactor|itkMultiThreader refactor]]<br />
* [[Documentation/Nightly/Developers/Tutorials/MigrationGuide#SimpleFastMutexLock.2C_FastMutexLock_and_MutexLock_are_deprecated|SimpleFastMutexLock, FastMutexLock and MutexLock are deprecated]]<br />
<br />
If completely removing support for ITKv4 and only supporting ITKv5 is not possible. It is possible to conditionally include the ITKv4 code.<br />
<br />
For example:<br />
<br />
<pre><br />
#if ITK_VERSION_MAJOR >= 5<br />
itk::ITK_THREAD_RETURN_TYPE<br />
#else<br />
ITK_THREAD_RETURN_TYPE<br />
#endif<br />
</pre><br />
<br />
Example of commits:<br />
* ResampleDTIlogEuclidean<br />
** [https://github.com/NIRALUser/ResampleDTIlogEuclidean/commit/f779bf775aad3c4e1493ed8fa722b32623ac9561 ResampleDTIlogEuclidean@f779bf7] COMP: Use std::mutex instead of deprecated ITK implementation<br />
** [https://github.com/NIRALUser/ResampleDTIlogEuclidean/commit/ed5093b15d0ded34db08978c6c5933c36e40b803 ResampleDTIlogEuclidean@ed5093b] BUG: ITKv5: Fix tests updating ITK filters to use ITKv5 dynamic multi-threading<br />
* PETTumorSegmentation<br />
** [https://github.com/QIICR/PETTumorSegmentation/pull/18/commits/4aead376ed1d909310d43496dd349e8f3b8c8210 PETTumorSegmentation PR#18] BUG: Add support for ITKv5 dynamic multithreader <br />
** [https://github.com/QIICR/PETTumorSegmentation/pull/18/commits/814a689501ab06d70d3ee3d561c14674e724a0d2 PETTumorSegmentation PR#18] COMP: Support ITKv5 refactored threading models<br />
<br />
===Slicer 5.0: Python2 to Python3===<br />
<br />
Depending on the complexity of the extension, two approaches shall be considered:<br />
* code base common to Slicer 4.10 and Slicer 5.0. This means backward compatibility with the latest release is maintained.<br />
* specific branch for each Slicer version (e.g <tt>master-4.10</tt> and <tt>master</tt>)<br />
<br />
<br />
'''Example of commits'''<br />
<br />
* [http://viewvc.slicer.org/viewvc.cgi/Slicer4?view=revision&revision=28079 Slicer r28079] STYLE: Update python classes to follow new-style<br />
* [https://github.com/fedorov/MultiVolumeImporter/commit/f9917b237c3bc3255e3c7677dc9af351dc2325e1 MultiVolumeImporter@f9917b2] STYLE: Apply lib2to3.fixes.fix_idioms to support python3<br />
* [https://github.com/fedorov/MultiVolumeImporter/commit/3edd1bc593f178f11aa92d9baa62685bc96ac540 MultiVolumeImporter@3edd1bc] ENH: Support for Python3 <br />
* [https://github.com/slicersalt/ShapeVariationAnalyzer/commit/42343774c44dc78b8560fc0a9e33e3a6018cb6e1 ShapeVariationAnalyzer@4234377] STYLE: Update python scripts for python 3.x<br />
* [https://github.com/QIICR/PETTumorSegmentation/pull/18/commits/b298ab8e672e1ab52ae6b189a34d9930ccac19ba PETTumorSegmentation PR#18] BUG: Add support for Python 3 <br />
<br />
<br />
'''Step 1'''<br />
<br />
Understanding the scope of changes needed to support Python 3 can be done by:<br />
<br />
<ol start="1" style="list-style-type: decimal;"><br />
<li>installing future package (see https://python-future.org/)<br />
<pre><br />
Slicer_DIR=/path/to/Slicer-SuperBuild/Slicer-build<br />
${Slicer_DIR}/../python-install/bin/PythonSlicer -m pip install future<br />
</pre><br />
</li><br />
<li>applying all fixes<br />
<pre><br />
for f in `find ./ -name "*.py"`; do \<br />
${Slicer_DIR}/../python-install/bin/PythonSlicer \<br />
--launch futurize --nobackups --write $f; \<br />
done<br />
</pre><br />
<br />
<small>{{note}} Alternatively the <tt>future</tt> package could be installed in a regular python environment and used from there.<br />
<br />
<pre><br />
for f in `find ./ -name "*.py"`; do \<br />
futurize --nobackups --write $f; \<br />
done<br />
</pre><br />
</small><br />
<br />
</li><br />
</ol><br />
<br />
This first step will apply the following transformations:<br />
<pre><br />
lib2to3.fixes.fix_apply<br />
lib2to3.fixes.fix_dict<br />
lib2to3.fixes.fix_except<br />
lib2to3.fixes.fix_exec<br />
lib2to3.fixes.fix_exitfunc<br />
lib2to3.fixes.fix_filter<br />
lib2to3.fixes.fix_funcattrs<br />
lib2to3.fixes.fix_getcwdu<br />
lib2to3.fixes.fix_has_key<br />
lib2to3.fixes.fix_input<br />
lib2to3.fixes.fix_intern<br />
lib2to3.fixes.fix_isinstance<br />
lib2to3.fixes.fix_itertools<br />
lib2to3.fixes.fix_itertools_imports<br />
lib2to3.fixes.fix_long<br />
lib2to3.fixes.fix_map<br />
lib2to3.fixes.fix_methodattrs<br />
lib2to3.fixes.fix_ne<br />
lib2to3.fixes.fix_next<br />
lib2to3.fixes.fix_nonzero<br />
lib2to3.fixes.fix_numliterals<br />
lib2to3.fixes.fix_operator<br />
lib2to3.fixes.fix_paren<br />
lib2to3.fixes.fix_raw_input<br />
lib2to3.fixes.fix_reduce<br />
lib2to3.fixes.fix_renamesSlicer migration guide describes how to update the code. See https://www.slicer.org/wiki/Documentation/Nightly/Developers/Tutorials/MigrationGuide/SlicerExtension#Slicer_5.0:_Python2_to_Python3<br />
lib2to3.fixes.fix_repr<br />
lib2to3.fixes.fix_standarderror<br />
lib2to3.fixes.fix_sys_exc<br />
lib2to3.fixes.fix_throw<br />
lib2to3.fixes.fix_tuple_params<br />
lib2to3.fixes.fix_types<br />
lib2to3.fixes.fix_xreadlines<br />
lib2to3.fixes.fix_zip<br />
libfuturize.fixes.fix_absolute_import<br />
libfuturize.fixes.fix_basestring<br />
libfuturize.fixes.fix_cmp<br />
libfuturize.fixes.fix_division_safe<br />
libfuturize.fixes.fix_execfile<br />
libfuturize.fixes.fix_future_builtins<br />
libfuturize.fixes.fix_future_standard_library<br />
libfuturize.fixes.fix_future_standard_library_urllib<br />
libfuturize.fixes.fix_metaclass<br />
libfuturize.fixes.fix_next_call<br />
libfuturize.fixes.fix_object<br />
libfuturize.fixes.fix_print_with_import<br />
libfuturize.fixes.fix_raise<br />
libfuturize.fixes.fix_unicode_keep_u<br />
libfuturize.fixes.fix_xrange_with_import<br />
libpasteurize.fixes.fix_newstyle<br />
</pre><br />
<br />
expect these two:<br />
<br />
<pre><br />
lib2to3.fixes.fix_idioms<br />
lib2to3.fixes.fix_ws_comma<br />
</pre><br />
<br />
<small>{{note}} If the number of changes is large, you should consider applying each fixes independently.</small><br />
<br />
<br />
'''Step 2'''<br />
<br />
Not all changes applied automatically should be integrated. Most of the changes introducing imports from the <tt>future</tt> python package can simply be removed or removed after making use of <tt>try/except</tt> or check of <tt>sys.version_info[0]</tt>.<br />
<br />
* Systematic conversion of keys, values or items from dictionaries do not need to be systematically converted from <tt>dict_keys</tt>/<tt>dict_values</tt>/<tt>dict_items</tt> to <tt>list</tt>. For example, this Slicer [http://viewvc.slicer.org/viewvc.cgi/Slicer4?view=revision&revision=28077 r28077] reverted some of the automatic changes applied by the future CLI.<br />
<br />
* Unless python classes implement the object functions <tt>next</tt> and <tt>__unicode__</tt> specific to Python 2, nothing specific should be done and <tt><b>from builtins import object</b></tt> can be removed. Automatic removal of imports can also be automated doing:<br />
<br />
for f in `find ./ -name "*.py"`; do \<br />
sed -i '/from builtins import object/ d' $f; \<br />
done<br />
<br />
* Unless there are performances issue associated with using <tt>range</tt> in Python 2, the <tt><b>from builtins import range</b></tt> can be removed:<br />
<br />
for f in `find ./ -name "*.py"`; do \<br />
sed -i '/from builtins import range/ d' $f; \<br />
done<br />
<br />
* If there are performance issue with using <tt>range</tt> in Python 2, the following could also be done:<br />
<br />
import sys<br />
if sys.version_info[0] == 2:<br />
range = xrange<br />
<br />
* Use of aliases can also be avoided by removing<br />
<br />
from future import standard_library<br />
standard_library.install_aliases()<br />
<br />
and instead doing something like this:<br />
<br />
try:<br />
import queue<br />
except ImportError:<br />
import Queue as queue<br />
<br />
For a complete list aliases, see https://python-future.org/reference.html#module-future.standard_library<br />
<br />
* Use of <tt>old_div()</tt> function can generally be avoided by using <tt>int()</tt><br />
<br />
'''Step 3'''<br />
<br />
The <tt>lib2to3.fixes.fix_idioms</tt> transformation should explicitly be applied:<br />
<br />
<pre><br />
for f in `find ./ -name "*.py"`; do \<br />
${Slicer_DIR}/../python-install/bin/PythonSlicer \<br />
--launch futurize -f lib2to3.fixes.fix_idioms --nobackups --write $f; \<br />
done<br />
</pre><br />
<br />
===Slicer 5.0: Python2 to Python3 (EditorEffect imports)===<br />
<br />
'''Error:'''<br />
<br />
<pre><br />
from EditorLib import EditorLib<br />
ImportError: cannot import name 'EditorLib'<br />
</pre><br />
<br />
Import of Editor classes should be updated. For example, see [http://viewvc.slicer.org/viewvc.cgi/Slicer4/trunk/Utilities/Templates/Modules/ScriptedEditorEffect/TemplateKeyEffect.py?r1=28122&r2=28121&pathrev=28122 r28122].<br />
<br />
<br />
'''Before:'''<br />
<br />
<pre><br />
import EditorLib<br />
from EditorLib.EditOptions import HelpButton<br />
from EditorLib.EditOptions import EditOptions<br />
from EditorLib import EditUtil<br />
</pre><br />
<br />
<pre><br />
class TemplateKeyEffectOptions(EditorLib.LabelEffectOptions):<br />
[...]<br />
</pre><br />
<br />
<br />
'''After:'''<br />
<br />
<pre><br />
import EditorLib<br />
from EditorLib import EditOptions, HelpButton<br />
from EditorLib import EditUtil<br />
from EditorLib import LabelEffectOptions, LabelEffectTool, LabelEffectLogic, LabelEffect<br />
</pre><br />
<br />
<pre><br />
class TemplateKeyEffectOptions(LabelEffectOptions):<br />
[...]<br />
</pre><br />
<br />
===Slicer 5.0: Python packaging dicom to pydicom===<br />
<br />
The [https://pypi.org/project/dicom/ dicom] python package has transitioned to become the [https://pypi.org/project/pydicom/ pydicom] python package. The dicom package is no longer included and all previous usages should be transitioned to use the pydicom package. See the [https://pydicom.github.io/pydicom/stable/transition_to_pydicom1.html Transition to pydicom 1.x] migration guide.<br />
<br />
===Slicer 4.9: Explicit include of ExternalProject module not needed anymore===<br />
<br />
Following [http://viewvc.slicer.org/viewvc.cgi/Slicer4?view=revision&revision=26984 r26984], calling <tt>find_package(Slicer REQUIRED)</tt> and <tt>include(${Slicer_USE_FILE})</tt> ensure the <tt>ExternalProject</tt> CMake module is included.<br />
<br />
Note that <tt>ExternalProjectDependency</tt> CMake module is also included.<br />
<br />
===Slicer 4.9: Explicit passing of CMAKE_OSX_* variables not needed anymore===<br />
<br />
Following [http://viewvc.slicer.org/viewvc.cgi/Slicer4?view=revision&revision=26983 r26983], calling <tt>find_package(Slicer REQUIRED)</tt> and <tt>include(${Slicer_USE_FILE})</tt> initializes <tt>CMAKE_OSX_*</tt> variables and ensures the variables <tt>CMAKE_OSX_ARCHITECTURES</tt>, <tt>CMAKE_OSX_SYSROOT</tt> and <tt>CMAKE_OSX_DEPLOYMENT_TARGET</tt> are passed to all external projects when configuring SuperBuild based extension.<br />
<br />
===Slicer 4.9: Explicit initialization of CMAKE_BUILD_TYPE not needed anymore===<br />
<br />
Following [http://viewvc.slicer.org/viewvc.cgi/Slicer4?view=revision&revision=26978 r26978], calling <tt>find_package(Slicer REQUIRED)</tt> and <tt>include(${Slicer_USE_FILE})</tt> initializes <tt>CMAKE_BUILD_TYPE</tt> and ensures the variables <tt>CMAKE_BUILD_TYPE</tt> and <tt>CMAKE_CONFIGURATION_TYPES</tt> are passed to all external projects when configuring SuperBuild based extension.<br />
<br />
The module <tt>SlicerInitializeBuildType</tt> is automatically included in <tt>UseSlicer</tt> CMake module.<br />
<br />
===Slicer 4.9: Subversion not required anymore===<br />
<br />
Following [http://viewvc.slicer.org/viewvc.cgi/Slicer4?view=revision&revision=27060 r27060], Subversion is not required anymore.<br />
<br />
===Slicer 4.9: Support EP_GIT_PROTOCOL and use of ExternalProject_SetIfNotDefined for setting GIT_REPOSITORY, GIT_TAG and alike===<br />
<br />
Following [http://viewvc.slicer.org/viewvc.cgi/Slicer4?view=revision&revision=26957 r26957], extension may use the following convention to define <tt>GIT_REPOSITORY</tt> and <tt>GIT_TAG</tt>, this allows developer to override the value before the first configuration by setting the corresponding environment variable, or by explicitly configuring the project with that variable.<br />
<br />
The option <tt>EP_GIT_PROTOCOL</tt> is also already set in <tt>ExternalProjectDependency</tt> module included by Slicer and its value is updated based on the <tt>&lt;SUPERBUILD_TOPLEVEL_PROJECT&gt;_USE_GIT_PROTOCOL</tt> option.<br />
<br />
<pre><br />
ExternalProject_SetIfNotDefined(<br />
${CMAKE_PROJECT_NAME}_${proj}_GIT_REPOSITORY<br />
"${EP_GIT_PROTOCOL}://github.com/jcfr/shape4D.git"<br />
QUIET<br />
)<br />
<br />
ExternalProject_SetIfNotDefined(<br />
${CMAKE_PROJECT_NAME}_${proj}_GIT_TAG<br />
"12fef84ca2a56feffc59d8159bdadd2ce4a4138e" # slicersalt-2018-01-22-c74c766a4c<br />
QUIET<br />
)<br />
</pre><br />
<br />
See:<br />
* https://cmake-artichoke.readthedocs.io/en/latest/ExternalProjectDependency.html#variable:EP_GIT_PROTOCOL<br />
* https://cmake-artichoke.readthedocs.io/en/latest/ExternalProjectDependency.html#function:ExternalProject_SetIfNotDefined<br />
<br />
===Slicer 4.9: Use ExternalProject_AlwaysConfigure to force reconfigure of inner project===<br />
<br />
Following [http://viewvc.slicer.org/viewvc.cgi/Slicer4?view=revision&revision=26551 r26551], the function <tt>ExternalProject_AlwaysConfigure</tt> may be used to ensure the inner project is always reconfigured.<br />
<br />
Using the `BUILD_ALWAYS` option supported by <tt>ExternalProject_Add</tt> will not have the intended effect.<br />
<br />
See https://cmake-artichoke.readthedocs.io/en/latest/ExternalProjectDependency.html#function:ExternalProject_AlwaysConfigure<br />
<br />
===Slicer 4.9: Specifying external projects to install in SuperBuild extension===<br />
<br />
Following [http://viewvc.slicer.org/viewvc.cgi/Slicer4?view=revision&revision=27267 r27267] and [http://viewvc.slicer.org/viewvc.cgi/Slicer4?view=revision&revision=27232 r27232], the following should be used to ensure<br />
the extension can also be bundled into a Slicer custom application. When bundled, the variable <tt>${EXTENSION_NAME}_CPACK_INSTALL_CMAKE_PROJECTS</tt> is then used to update the application's list of project to install. <br />
<br />
<pre><br />
#-----------------------------------------------------------------------------<br />
set(EXTENSION_CPACK_INSTALL_CMAKE_PROJECTS)<br />
#list(APPEND EXTENSION_CPACK_INSTALL_CMAKE_PROJECTS "${Foo_DIR};Foo;RuntimeLibraries;/")<br />
set(${EXTENSION_NAME}_CPACK_INSTALL_CMAKE_PROJECTS "${EXTENSION_CPACK_INSTALL_CMAKE_PROJECTS}" CACHE STRING "List of external projects to install" FORCE)<br />
<br />
#-----------------------------------------------------------------------------<br />
list(APPEND CPACK_INSTALL_CMAKE_PROJECTS "${CMAKE_BINARY_DIR};${EXTENSION_NAME};ALL;/")<br />
list(APPEND CPACK_INSTALL_CMAKE_PROJECTS "${${EXTENSION_NAME}_CPACK_INSTALL_CMAKE_PROJECTS}")<br />
include(${Slicer_EXTENSION_GENERATE_CONFIG})<br />
include(${Slicer_EXTENSION_CPACK})<br />
</pre><br />
<br />
===Slicer 4.9: Generating (Extension)Config.cmake===<br />
<br />
Initially introduced in [http://viewvc.slicer.org/viewvc.cgi/Slicer4?view=revision&revision=25944 r25944], and later improved in [http://viewvc.slicer.org/viewvc.cgi/Slicer4?view=revision&revision=25991 r25991], including <tt>${Slicer_EXTENSION_GENERATE_CONFIG}</tt> ensure a config is generated and allow an extension to import targets from another extension by using <tt>find_package(ExtensionName REQUIRED)</tt>.<br />
<br />
<pre><br />
[...]<br />
<br />
include(${Slicer_EXTENSION_GENERATE_CONFIG})<br />
include(${Slicer_EXTENSION_CPACK})<br />
</pre><br />
<br />
===Slicer 4.9: Initializing <projectName>_BUILD_SLICER_EXTENSION option: Standalone vs Slicer extension build===<br />
<br />
The following snippet allows to automatically initialize <tt><projectName>_BUILD_SLICER_EXTENSION</tt> to <tt>ON</tt> if <tt>Slicer_DIR</tt> is defined.<br />
<br />
<pre><br />
#-----------------------------------------------------------------------------<br />
# Standalone vs Slicer extension option<br />
#-----------------------------------------------------------------------------<br />
<br />
# This option should be named after the project name, it corresponds to the<br />
# option set to ON when the project is build by the Slicer Extension build<br />
# system.<br />
<br />
set(_default OFF)<br />
set(_reason "${PROJECT_NAME}_BUILD_SLICER_EXTENSION is ON")<br />
if(NOT DEFINED ${PROJECT_NAME}_BUILD_SLICER_EXTENSION AND DEFINED Slicer_DIR)<br />
set(_default ON)<br />
set(_reason "Slicer_DIR is SET")<br />
endif()<br />
<br />
option(${PROJECT_NAME}_BUILD_SLICER_EXTENSION "Build as a Slicer Extension" ${_default})<br />
<br />
set(_msg "Checking if building as a Slicer extension")<br />
message(STATUS ${_msg})<br />
if(${PROJECT_NAME}_BUILD_SLICER_EXTENSION)<br />
message(STATUS "${_msg} - yes (${_reason})")<br />
else()<br />
message(STATUS "${_msg} - no (${PROJECT_NAME}_BUILD_SLICER_EXTENSION is OFF)")<br />
endif()<br />
mark_as_superbuild(${PROJECT_NAME}_BUILD_SLICER_EXTENSION:BOOL)<br />
</pre></div>Jamesobutler