Difference between revisions of "Documentation/Nightly/Developers/Tutorials/MigrationGuide"
Tag: 2017 source edit |
|||
(18 intermediate revisions by 3 users not shown) | |||
Line 1: | Line 1: | ||
<noinclude>{{documentation/versioncheck}}</noinclude><includeonly>[[Documentation/{{documentation/version}}/Developers/Tutorials/MigrationGuide|Migration Guide]]<br> | <noinclude>{{documentation/versioncheck}}</noinclude><includeonly>[[Documentation/{{documentation/version}}/Developers/Tutorials/MigrationGuide|Migration Guide]]<br> | ||
− | : How to update Slicer extension code following | + | : How to update Slicer extension code following breaking changes in Slicer or dependent toolkits (VTK, ITK, Qt, ...) |
</includeonly><noinclude> | </includeonly><noinclude> | ||
<span style="color: #555555; font-size: 18px; font-weight: bold;" id="Source">Migration Guide</span> | <span style="color: #555555; font-size: 18px; font-weight: bold;" id="Source">Migration Guide</span> | ||
---- | ---- | ||
− | * [[Documentation/ | + | * [[Documentation/Nightly/Developers/Tutorials/MigrationGuide/Slicer50|Work-in-progress: Transition plans for Slicer 4.10, and major changes in Slicer 5.0]] |
− | + | ||
− | + | {{:Documentation/Nightly/Developers/Tutorials/MigrationGuide/Slicer}} | |
+ | {{:Documentation/Nightly/Developers/Tutorials/MigrationGuide/SlicerExtension}} | ||
+ | {{:Documentation/Nightly/Developers/Tutorials/MigrationGuide/ObsoleteCodeRemoval}} | ||
+ | {{:Documentation/Nightly/Developers/Tutorials/MigrationGuide/ITK4-to-ITK5}} | ||
+ | {{:Documentation/Nightly/Developers/Tutorials/MigrationGuide/VTK8-to-VTK9}} | ||
+ | {{:Documentation/Nightly/Developers/Tutorials/MigrationGuide/VTK7-Qt4-to-VTK8-Qt5}} | ||
+ | {{:Documentation/Nightly/Developers/Tutorials/MigrationGuide/VTK6-to-VTK7}} | ||
+ | {{:Documentation/Nightly/Developers/Tutorials/MigrationGuide/VTK5-to-VTK6}} | ||
</noinclude> | </noinclude> |
Latest revision as of 19:38, 18 May 2021
Home < Documentation < Nightly < Developers < Tutorials < MigrationGuide
For the latest Slicer documentation, visit the read-the-docs. |
Migration Guide
Contents
- 1 Slicer backward incompatible changes
- 1.1 Slicer 5.3: Organization name and domain changed from NA-MIC to slicer.org
- 1.2 Slicer 5.3: Removed Annotation module
- 1.3 Slicer 5.0: API changes since 4.10
- 1.3.1 Supporting only Python 3.6 and above
- 1.3.2 Python 2 to Python 3
- 1.3.3 Interactor styles
- 1.3.4 slicer.util functions
- 1.3.5 Markups
- 1.3.6 Segmentations
- 1.3.6.1 Erase the contents of a single segment
- 1.3.6.2 Set labelmap in a segment
- 1.3.6.3 Move a segment from a shared labelmap to a separate layer
- 1.3.6.4 Combine all binary labelmaps to as few layers as possible
- 1.3.6.5 Get a modifiable shared labelmap
- 1.3.6.6 Export segments to models
- 1.3.6.7 Smoothing effect
- 1.3.7 Volume rendering
- 1.3.8 Extract skeleton
- 1.3.9 MRML node copy API improvements
- 1.3.10 Removed classes
- 1.4 Slicer 5.0: Fiducial List was renamed to Point List
- 1.5 Slicer 5.0: SliceIntersectionVisibility was moved from vtkMRMLSliceCompositeNode to vtkMRMLSliceDisplayNode
- 1.6 Slicer 5.0: SlicerPython was removed. Use PythonSlicer instead
- 1.7 Slicer 5.0: Application must be installed in writable location to install extensions
- 1.8 Slicer 5.0: Models are saved in LPS coordinate system by default
- 1.9 Slicer 5.0: CLI module descriptor XML files assume LPS coordinate system by default
- 1.10 Slicer 5.0: Sequences extension has been merged into Slicer core
- 1.11 Slicer 5.0: FreeSurfer support has been removed from Slicer core
- 1.12 Slicer 5.0: Removed Editor module
- 1.13 Slicer 5.0: Removed Charts and DoubleArrays module
- 1.14 Slicer 5.0 : Avoid typedef of anonymous structure
- 1.15 Slicer 5.0 : Temporary path
- 1.16 Slicer 5.0 : Always prefer executable CLIs
- 1.17 Slicer 5.0 : SlicerApp-real is a console application on Windows
- 1.18 Slicer 4.11: Variable CMAKE_DEFAULT_BUILD_TYPE renamed to Slicer_DEFAULT_BUILD_TYPE
- 1.19 Slicer 4.11: teem python module renamed to vtkTeem, explicit import required
- 1.20 Slicer 4.11: Display window/level (brightness/contrast) adjustment
- 1.21 Slicer 4.10: Registration of runTest function done in ScriptedLoadableModule base class
- 1.22 Slicer 4.9: Update of VTK version from 9.0 to 8.2
- 1.23 Slicer 4.9: ITK_LEGACY_REMOVE is now OFF
- 1.24 Slicer 4.9: vtkMRMLPlotDataNode renamed to vtkMRMLPlotSeriesNode
- 1.25 Slicer 4.9: CMake: Module MIDAS not available
- 1.26 Slicer 4.9: CMake: Module SlicerMacroCheckExternalProjectDependency not available
- 1.27 Slicer 4.9: CMake: Module SlicerMacroEmptyExternalProject not available
- 1.28 Slicer 4.9: CMake: Module SlicerBlockSetCMakeOSXVariables not available
- 1.29 Slicer 4.9: Application: isRelease() function not available
- 1.30 Slicer 4.9: slicer.util.getNode() raises exception if node not found
- 1.31 Slicer 4.8: Application: isRelease() function not available or deprecated
- 1.32 Slicer Python Module: modulewidget and others removed.
- 1.33 MRML: Slicer 4.6: Moved up vtkMRMLStorableNode in the MRML node hierarchy.
- 1.34 MRML: Slicer 4.5: Introduction of vtkMRMLLabelMapVolumeNode
- 1.35 CLI: Slicer 4.3: Add ITKFactoryRegistration library centralizing ITK IO factory registration
- 2 Slicer Extension updates
- 2.1 Slicer 5.0: Supporting both VTK 8.0 and VTK 9.0
- 2.2 Slicer 5.0: ITKv4 to ITKv5
- 2.3 Slicer 5.0: Python2 to Python3
- 2.4 Slicer 5.0: Python2 to Python3 (EditorEffect imports)
- 2.5 Slicer 5.0: Python packaging dicom to pydicom
- 2.6 Slicer 4.9: Explicit include of ExternalProject module not needed anymore
- 2.7 Slicer 4.9: Explicit passing of CMAKE_OSX_* variables not needed anymore
- 2.8 Slicer 4.9: Explicit initialization of CMAKE_BUILD_TYPE not needed anymore
- 2.9 Slicer 4.9: Subversion not required anymore
- 2.10 Slicer 4.9: Support EP_GIT_PROTOCOL and use of ExternalProject_SetIfNotDefined for setting GIT_REPOSITORY, GIT_TAG and alike
- 2.11 Slicer 4.9: Use ExternalProject_AlwaysConfigure to force reconfigure of inner project
- 2.12 Slicer 4.9: Specifying external projects to install in SuperBuild extension
- 2.13 Slicer 4.9: Generating (Extension)Config.cmake
- 2.14 Slicer 4.9: Initializing <projectName>_BUILD_SLICER_EXTENSION option: Standalone vs Slicer extension build
- 3 Obsolete Code Removal
- 3.1 Qt>=5.0.0: Remove obsolete code supporting Qt4 plugin infrastructure (C++)
- 3.2 Qt>=5.0.0: Simpler use of QHeaderView::setSectionResizeMode
- 3.3 C++11: Update source code to use nullptr
- 3.4 C++11: Update source code to use override
- 3.5 C++11: Update source code to use = delete
- 3.6 C++11: Update source code to use = default
- 3.7 C++11: Use default member initialization
- 4 Transition from ITK4 to ITK5
- 5 Transition from VTK 8.0 to VTK 9.0
- 6 Transition from VTK7-Qt4 to VTK8-Qt5
- 6.1 Slicer SuperBuild Extension: Enable C++11 in external projects
- 6.2 Qt5: Update loadable modules to use new plugin macros
- 6.3 Qt5: Update DesignerPlugin to use QtUiPlugin/QDesignerCustomWidgetInterface
- 6.4 Qt5: Update DesignerPlugin to use QtUiPlugin/QDesignerCustomWidgetCollectionInterface
- 6.5 Qt5: any use of QWebKit needs to switch to QWebEngine
- 6.6 Qt5: QVTKOpenGLWidget
- 6.7 Qt5: Fix error: 'class QString' has no member named 'toAscii'
- 6.8 Qt5: Fix error: ‘class QHeaderView’ has no member named ‘setResizeMode’
- 6.9 VTK8: Use hierarchy files for VTK Python wrapping
- 6.10 VTK8: Use of vtkTypeMacro requires to use the correct base class
- 6.11 VTK8: Copy constructor and equal operator should be disabled
- 6.12 VTK8: Call InitializeObjectBase() in vtkObject New() methods
- 6.13 VTK8: Add C++11 keywords
- 6.14 VTK8: vtkWindowToImageFilter::SetMagnification() is deprecated
- 6.15 VTK8: vtkInstantiator is deprecated
- 6.16 VTK 8.2: Signature of vtkFSLookupTable::MapValue updated
- 6.17 Slicer scripted module initialization steps after application startup
- 7 Transition from VTK6 to VTK7
- 7.1 Deprecated the vtkStreamer class hierarchy
- 7.2 Deprecated vtkMatrix4x4::operator[] method
- 7.3 Removed vtksys/ios, vtksys/stl compatibility layers
- 7.4 vtkDataArray refactored
- 7.5 Deprecated pipeline update methods
- 7.6 Updated Python wrapping
- 7.7 vtkMTimeType
- 7.8 vtkStandardNewMacro expects vtkObjectFactory.h
- 8 Transition from VTK5 to VTK6
Slicer backward incompatible changes
Slicer 5.3: Organization name and domain changed from NA-MIC to slicer.org
To address the inconsistency between organization name used for settings files and for macOS bundle identifier, the 3D Slicer application has updated the Slicer_ORGANIZATION_NAME
from "NA-MIC" to "slicer.org" and Slicer_ORGANIZATION_DOMAIN
from www.na-mic.org
to slicer.org
. 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 slicer.org rather than at www.na-mic.org.
Slicer settings will now be under a Slicer directory location rather than NA-MIC. Please review the Settings File Location documentation for details about the settings location on various platforms.
Slicer 5.3: Removed Annotation module
Annotations module, which provides `vtkMRMLAnnotationROI` and `vtkMRMLAnnotationRuler` nodes have been deprecated since April 2021 and is to be removed in Slicer-4.3.
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.
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.
Tips for updating a module to use markups:
- In node selectors, wherever `vtkMRMLAnnotationROINode` is accepted, add `vtkMRMLMarkupsLineNode` _before_ it (so they are both accepted, but markups are preferred)
- In node selectors, wherever `vtkMRMLAnnotationRuler` is accepted, add `vtkMRMLMarkupsLine` _before_ it (so they are both accepted, but markups are preferred)
- For ROIs:
- When only non-rotated ROIs are used: you can still use `GetXYZ()` and `GetRadiusXYZ()` methods work the same way for markups ROI
- 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()`).
- For rulers:
- Use `GetNthControlPointPosition(0)` and `GetNthControlPointPosition(1)` methods to get the endpoints of the line.
- Use `GetNumberOfDefinedControlPoints()` method to check if both endpoints of the line are defined.
- Use `GetMeasurement('length').GetValue()` to get the line length (or for the displayed string, with units: `getNode('L').GetMeasurement('length').GetValueWithUnitsAsPrintableString()`)
Slicer 5.0: API changes since 4.10
- Removed protected method vtkMRMLModelDisplayableManager::FindPickedDisplayNodeFromMesh
Supporting only Python 3.6 and above
Slicer python code has been updated to support Python 3.6 and above syntax using pyupgrade to automatically update the syntax.
Install pyupgrade: PythonSlicer -m pip install pyupgrade
- Running:
- On 1 file:
PythonSlicer -m pyupgrade --py36-plus MyPythonFile.py
- 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
PythonSlicer pyupgrade-script.py
- On 1 file:
# pyupgrade-script.py
import os
import subprocess
search_directory = "C:/Users/MyUserName/Documents/GitHub/Slicer"
for root, _, files in os.walk(search_directory):
for file_item in files:
file_path = os.path.join(root, file_item)
if os.path.isfile(file_path) and file_path.endswith(".py"):
subprocess.call(["PythonSlicer", "-m", "pyupgrade", "--py36-plus", file_path])
Python 2 to Python 3
Slicer core has been updated to only support Python 3.
C++ classes and python scripts have been updated to use idioms and constructs only available in Python 3.
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.
Updates specific to extensions are discussed in Documentation/Nightly/Developers/Tutorials/MigrationGuide#Slicer_5.0:_Python2_to_Python3
Interactor styles
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.
- vtkSliceViewInteractorStyle renamed to vtkMRMLSliceDViewInteractorStyle to reflect that it uses MRML classes directly.
- vtkThreeDViewInteractorStyle renamed to vtkMRMLThreeDViewInteractorStyle to reflect that it uses MRML classes directly.
slicer.util functions
- 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.
- Old way of loading a node and get it in a variable:
volumeNode = slicer.util.loadVolume('path/to/volume.nrrd', returnNode=True)[1]
- New way of loading a node and get it in a variable:
volumeNode = slicer.util.loadVolume('path/to/volume.nrrd')
- Old way of loading a node and get it in a variable:
Markups
- vtkCommand::Modified 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 vtkMRMLMarkupsNode::PointAddedEvent, vtkMRMLMarkupsNode::PointRemovedEvent, vtkMRMLMarkupsNode::PointModifiedEvent events. See example in Script repository.
- vtkMRMLMarkupsNode::MarkupAddedEvent is renamed to PointPositionDefinedEvent. There is a similar event, vtkMRMLMarkupsNode::PointAddedEvent, which is called even when preview point is created.
- vtkMRMLMarkupsNode::MarkupRemovedEvent is renamed to vtkMRMLMarkupsNode::PointPositionUndefinedEvent. There is a similar event, vtkMRMLMarkupsNode::PointRemovedEvent, which is called even when preview point is removed.
- vtkMRMLMarkupsNode::NthMarkupModifiedEvent is replaced by vtkMRMLMarkupsNode::PointModifiedEvent
- During placement of markups, a preview markup point is created. If number of already placed markup points needs to be determined then
GetNumberOfDefinedControlPoints()
method can be used. - GetDefaultMarkups...() and SetDefaultMarkups...() methods are removed. Instead default display node can be accessed by GetDefaultMarkupsDisplayNode() method and default values can be get/set in that class.
- vtkMRMLMarkupsNode::GetNthMarkupSelected() is replaced by GetNthControlPointSelected()
- vtkMRMLMarkupsNode::PointPositionDefinedEvent event is added. This event is invoked whenever position is defined for a new point.
- vtkMRMLMarkupsNode::PointPositionUndefinedEvent event is added. This event is invoked whenever point with defined position is removed (point is deleted or its position gets undefined).
- For more details, see vtkMRMLMarkupsNode
Segmentations
Binary labelmap segmentations can now be represented as shared labelmaps. The previous implementation of binary labelmaps was performance intensive as each labelmap was represented using a separate vtkDataObject. 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.
By default, newly created segments will now be contained on the same layer. Segments will only be separated into multiple layers if the user creates an overlapping segment when editing.
Segments are now saved as a 4D volume with shared 3D layers. For a segmentation that only uses one layer, the resulting image is a 3D volume. Before saving, the labelmaps will be collapsed into as few layers as possible.
- seg.nrrd files now contain two additional attributes for each segment: SegmentX_LabelValue and SegmentX_Layer
- The label value of a segment can be found using vtkSegment::GetLabelValue()
- Whether or not a segment is shared can be found using vtkSegmentation::IsSharedBinaryLabelmap()
- The other segments sharing the same labelmap can be found using vtkSegmentation::GetSegmentIDsSharingBinaryLabelmapRepresentation()
- Segment editor effects should generally use modifySelectedSegmentByLabelmap rather than SetBinaryLabelmapToSegment to manage layer separation
- Conversion rules now call PreConvert() and PostConvert() before and after conversion to perform pre and post processing steps on the segmentation as a whole
- The function signature for vtkSegmentationConverterRule::Convert now accepts a vtkSegment rather than two vtkDataObjects
- slicer.util.arrayFromSegment has been deprecated. slicer.util.arrayFromSegmentBinaryLabelmap and slicer.util.arrayFromSegmentInternalBinaryLabelmap can be used instead
Erase the contents of a single segment
segmentation = segmentationNode.GetSegmentation() segmentation.ClearSegment(segmentId)
Set labelmap in a segment
Directly, bypassing masking settings:
slicer.vtkSlicerSegmentationsModuleLogic.SetBinaryLabelmapToSegment(orientedImageDataToSet, segmentationNode, segmentId)
segmentation = segmentationNode.GetSegmentation() segmentation.SeparateSegmentLabelmap(segmentId)
Combine all binary labelmaps to as few layers as possible
segmentation = segmentationNode.GetSegmentation() segmentation.CollapseBinaryLabelmaps(forceToSingleLayer=false)
Get a read-only labelmap for a single segment:
labelmap = slicer.vtkOrientedImageData() segmentationNode.GetBinaryLabelmapRepresentation(segmentId, labelmap)
(similarly, use GetClosedSurfaceRepresentation with an additional vtk.vtkPolyData parameter to get a read-only surface mesh)
or
labelmapNumpyArray = slicer.util.arrayFromSegmentBinaryLabelmap(segmentationNode, segmentId)
labelmap = slicer.vtkOrientedImageData() segmentationNode.GetBinaryLabelmapInternalRepresentation(segmentId, labelmap)
(similarly, use GetClosedSurfaceInternalRepresentation to get a modifiable surface mesh)
or
labelmapNumpyArray = slicer.util.arrayFromSegmentInternalBinaryLabelmap(segmentationNode, segmentId)
Export segments to models
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. Documentation/Nightly See code example in Script repository.
Smoothing effect
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.
Volume rendering
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
volumeNode->AddAndObserveDisplayNodeID(volumeRenderingDisplayNode->GetID());
after both nodes are added to the scene.
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.
Extract skeleton
Command-line arguments of the module have been updated: - output image is now optional, therefore the output image file name must be specified using "--outputImage" argument - 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) - "--dontPrune" is renamed to "--fullTree" for clarity - centerline curve is saved in mrk.json format
MRML node copy API improvements
Slicer-4.10 and earlier had a single Copy() method, which had limitations: - 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) - 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)
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.
To make it easier to introduce this new method into existing classes, helper macros are implemented.
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).
If HasCopyContent macro is not added to a class then it cannot be recorded or replayed in Sequences module.
Removed classes
Classes removed due to removing legacy Editor module:
- vtkITKNewOtsuThresholdImageFilter is replaced by vtkITKImageThresholdCalculator
- vtkITKGrowCutSegmentationImageFilter is replaced by vtkImageGrowCutSegment (it will be replaced by the ITK implementation https://github.com/Slicer/Slicer/pull/5807)
- vtkITKTimeSeriesDatabase was removed, it was an incomplete class, not used anywhere
- vtkITKWandImageFilter was removed, vtkImageThresholdConnectivity (in VTK) can be used instead
- vtkImageConnectivity was removed, vtkImageThresholdConnectivity (in VTK) can be used instead
- vtkImageErode was removed, vtkImageDilateErode3D (in VTK) can be used instead
- vtkImageLabelChange was removed, vtkImageThreshold (in VTK) can be used instead
- vtkImageSlicePaint was replaced by logic built into qSlicerSegmentEditorPaintEffect
- vtkImageStash is replaced by vtkSegmentationHistory
- vtkPichonFastMarching moved to SegmentEditorExtraEffects extension (https://github.com/lassoan/SlicerSegmentEditorExtraEffects)
Classes removed due to removing Charts and DoubleArrays modules:
- vtkMRMLChartNode is replaced by vtkMRMLPlotNode
- vtkMRMLChartViewNode is replaced by vtkMRMLPlotViewNode
- vtkMRMLDoubleArrayNode is replaced by vtkMRMLTableNode (can store any number of columns, not just two)
- vtkMRMLDoubleArrayStorageNode is replaced by vtkMRMLTableStorageNode
- qMRMLChartView is replaced by qMRMLPlotView
- qMRMLChartViewControllerWidget is replaced by qMRMLPlotViewControllerWidget
- qMRMLChartWidget is replaced by qMRMLPlotWidget
Slicer 5.0: Fiducial List was renamed to Point List
To simplify terms used in Slicer, "Fiducial List" term was renamed to "Point List" on the user interface. The term in the API has not been changed to preserve backward compatibility.
See discussion of the topic here.
Slicer 5.0: SliceIntersectionVisibility was moved from vtkMRMLSliceCompositeNode to vtkMRMLSliceDisplayNode
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.
Scripts that previously used SliceIntersectionVisibility property will now fail with this error:
AttributeError: 'MRMLCore.vtkMRMLSliceCompositeNode' object has no attribute 'SetSliceIntersectionVisibility'
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
Slicer 5.0: SlicerPython was removed. Use PythonSlicer instead
Error message:
SlicerPython executable is obsolete and will be removed. Use PythonSlicer executable instead. For more details, see https://github.com/Slicer/Slicer/issues/4843
Solution:
Use PythonSlicer instead of SlicerPython
Background:
Python IDEs (specifically PyCharm, but potentially others) only recognize Python*.exe files as Python interpreters.
To allow using Slicer's Python interpreter in these IDEs, we had to add PythonSlicer, but kept SlicerPython around for not immediately breaking things.
This redundancy is confusing for users that we could resolve by simply removing SlicerPython for Slicer5.
References:
https://github.com/Slicer/Slicer/issues/4843
Slicer 5.0: Application must be installed in writable location to install extensions
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 here.
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 Slicer_STORE_SETTINGS_IN_APPLICATION_HOME_DIR:BOOL=OFF
when configuring your Slicer build.
Slicer 5.0: Models are saved in LPS coordinate system by default
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.
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.
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).
Slicer started embedding coordinate system name in mesh files a few years ago (see SPACE=RAS
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.
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.
If you encounter orientation issues when loading a model file, you have the following options:
- 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.
- 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.
- Option C: Write
SPACE=RAS
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 namedSPACE
) 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 here.
See more information, discussion of this topic on the Slicer forum.
Slicer 5.0: CLI module descriptor XML files assume LPS coordinate system by default
If 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.
Slicer 5.0: Sequences extension has been merged into Slicer core
Sequences extension has been merged into Slicer core, therefore extensions do not need to depend on Sequences extension anymore.
SequenceBrowser module has been merged into Sequences module, therefore previous code that used SequenceBrowser module now should use Sequences module instead.
Slicer 5.0: FreeSurfer support has been removed from Slicer core
The loading of FreeSurfer models and scalar overlays, as well as the FreeSurfer-specific color nodes, have been moved to the new SlicerFreeSurfer extension. Tutorials on how to use the FreeSurfer Importer module to load multiple files at once can be found on the SlicerFreeSurfer tutorial page.
Slicer 5.0: Removed Editor module
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.
Slicer 5.0: Removed Charts and DoubleArrays module
Charts and DoubleArrays module have been deprecated since about 2018 and got removed in November 2021. They are replaced by Plots and Tables modules.
Example of commits
- SlicerRT@8f9155f94 ENH: Update DVH module to use plots infrastructure instead of charts
Slicer 5.0 : Avoid typedef of anonymous structure
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 enabled-by-default warning for these cases, and rephrase our existing error for the case where we encounter the typedef name for linkage after we've already computed and used a wrong linkage in terms of the new rule.
To fix warning message similar to:
Slicer/Libs/MRML/Core/vtkMRMLTableStorageNode.h:95:17: warning: anonymous non-C-compatible type given name for linkage purposes by typedef declaration; add a tag name here [-Wnon-c-typedef-for-linkage] typedef struct ^ ColumnInfo Slicer/Libs/MRML/Core/vtkMRMLTableStorageNode.h:99:5: note: type is not C-compatible due to this default member initializer int ScalarType = VTK_STRING; ^~~~~~~~~~~~~~ Slicer/Libs/MRML/Core/vtkMRMLTableStorageNode.h:102:5: note: type is given name 'ColumnInfo' for linkage purposes by this typedef declaration } ColumnInfo; ^ For consistency, Use 'using' to a named structure definintion for all structures.
Replace code like this:
typedef struct { std::string ColumnName; std::vector<vtkAbstractArray*> RawComponentArrays; int ScalarType = VTK_STRING; std::vector<std::string> ComponentNames; std::string NullValueString; } ColumnInfo;
By this:
struct StructColumnInfo { std::string ColumnName; std::vector<vtkAbstractArray*> RawComponentArrays; int ScalarType = VTK_STRING; std::vector<std::string> ComponentNames; std::string NullValueString; }; using ColumnInfo = struct StructColumnInfo;
References:
https://reviews.llvm.org/D74103
Slicer 5.0 : Temporary path
Temporary path was stored redundantly application settings (Slicer.ini) in two keys: Modules/TemporaryDirectory
and TemporaryPath
. Modules/TemporaryDirectory
overwrote TemporaryPath
at startup, but when temporary path was set via the slicer.app.temporaryPath
then it was only written to TemporaryPath
.
Changed behavior so that only TemporaryPath
is used. Modules/TemporaryDirectory
is ignored.
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 (QDir::tempPath()
).
Slicer 5.0 : Always prefer executable CLIs
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.
Slicer 5.0 : SlicerApp-real is a console application on Windows
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.
SlicerApp-real.exe is now built as a console application (see #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.
To display console output: https://slicer.readthedocs.io/en/latest/developer_guide/debugging/overview.html#console-output-on-windows
To launch a command-line terminal using subprocess.Popen
that shows a new terminal, specify creationflags=subprocess.CREATE_NEW_CONSOLE
argument.
Slicer 4.11: Variable CMAKE_DEFAULT_BUILD_TYPE renamed to Slicer_DEFAULT_BUILD_TYPE
Setting the default build type for single config generator may be done setting Slicer_DEFAULT_BUILD_TYPE instead of CMAKE_DEFAULT_BUILD_TYPE.
Error message similar to:
CMake Error: Generator Visual Studio 15 2017 does not support variable CMAKE_DEFAULT_BUILD_TYPE but it has been specified.
References:
https://github.com/Slicer/Slicer/pull/4799
Slicer 4.11: teem python module renamed to vtkTeem, explicit import required
- Since the module provides VTK classes interfacing with "teem", the name is now representative of the class it contains.
- vtkTeem classes are expected to be used by explicitly importing the module.
Replace code like this:
import teem class CalculateTensorScalars(object): def __init__(self): self.dti_math = teem.vtkDiffusionTensorMathematics()
By this:
import vtkTeem class CalculateTensorScalars(object): def __init__(self): self.dti_math = vtkTeem.vtkDiffusionTensorMathematics()
Slicer 4.11: Display window/level (brightness/contrast) adjustment
- 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.
- 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).
- Auto window/level reset: activate "Window/level" mouse mode and double-click the left mouse button.
- 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.
- Removed class vtkImageBimodalAnalysis
Slicer 4.10: Registration of runTest function done in ScriptedLoadableModule base class
Following r27617:
- the
ScriptedLoadableModule
class takes care of registering therunTest
function. - the
runTest
function expectsmsec
keyword argument.
Error message similar to:
Traceback (most recent call last): File "/path/to/Slicer-SuperBuild/Slicer-build/bin/Python/slicer/ScriptedLoadableModule.py", line 205, in onReloadAndTest test(msec=int(slicer.app.userSettings().value("Developer/SelfTestDisplayMessageDelay")), **kwargs) TypeError: runTest() got an unexpected keyword argument 'msec' Reload and Test: Exception! runTest() got an unexpected keyword argument 'msec'
Replace code like this:
class sceneImport2428(ScriptedLoadableModule): [...] def __init__(self, parent): ScriptedLoadableModule.__init__(self, parent) parent.title = "..." [...] parent.acknowledgementText = "..." self.parent = parent # Add this test to the SelfTest module's list for discovery when the module # is created. Since this module may be discovered before SelfTests itself, # create the list if it doesn't already exist. try: slicer.selfTests except AttributeError: slicer.selfTests = {} slicer.selfTests['sceneImport2428'] = self.runTest def runTest(self): tester = sceneImport2428Test() tester.runTest() [...]
By this:
class sceneImport2428(ScriptedLoadableModule): [...] def __init__(self, parent): ScriptedLoadableModule.__init__(self, parent) parent.title = "..." [...] parent.acknowledgementText = "..." [...]
Slicer 4.9: Update of VTK version from 9.0 to 8.2
Following 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
At first, this VTK commit and its companion kitware/VTK@8a00b357e were both reverted from the 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:
- r27472: COMP: Update c++ classes to support building against VTK >= 9 and VTK >= 8.2
- r27473: COMP: Update VTK to include version change from 9.0 to 8.2. Fixes #4623
This means that code depending on VTK must also be updated to include similar fixes.
Replace this:
#if VTK_MAJOR_VERSION >= 9
By this:
#if VTK_MAJOR_VERSION >= 9 || (VTK_MAJOR_VERSION >= 8 && VTK_MINOR_VERSION >= 2)
and
Replace this:
#if VTK_MAJOR_VERSION < 9
By this:
#if VTK_MAJOR_VERSION <= 7 || (VTK_MAJOR_VERSION <= 8 && VTK_MINOR_VERSION <= 1)
Slicer 4.9: ITK_LEGACY_REMOVE is now OFF
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 ITK 4 migration guide before ITK 5 migration guide.
Slicer 4.9: vtkMRMLPlotDataNode renamed to vtkMRMLPlotSeriesNode
Plotting was improved in this commit
Replace this:
vtkMRMLPlotDataNode
By this:
vtkMRMLPlotSeriesNode
Slicer 4.9: CMake: Module MIDAS not available
The test infrastructure of your project should be updated to use ExternalData built-in CMake module instead of the specific MIDAS module.
See EMSegment commit r17150 for an example of transition.
This means that instead of using midas_add_test with the MIDAS{path/to/file.ext.md5} syntax for addressing the test data, the function ExternalData_add_target is used by specifying both DATA{path/to/file.ext} and a download target name.
Replace this:
midas_add_test(NAME test1 COMMAND ...) midas_add_test(NAME test2 COMMAND ...)
By this:
ExternalData_add_test(EMSegmentData NAME test1 COMMAND ...) ExternalData_add_test(EMSegmentData NAME test2 COMMAND ...) [...] ExternalData_add_target(EMSegmentData)
A key difference with the former approaches is that instead of adding two tests (one named
<testName>_fetchData to downoad the data and one running the test command), only one
test is added but a common download target is added at the end using ExternalData_add_target
function.
This means that test data can now be downloaded in parallel (and cached) at build time instead of testing time.
Slicer 4.9: CMake: Module SlicerMacroCheckExternalProjectDependency not available
Since the module was removed in r26992, consider updating
your build system to use CMake module ExternalProjectDependency
Slicer 4.9: CMake: Module SlicerMacroEmptyExternalProject not available
Since the module was removed in r26991
Replace this:
include(SlicerMacroEmptyExternalProject) [...] SlicerMacroEmptyExternalProject("${proj}" "${${proj}_DEPENDENCIES}")
By this:
include(ExternalProjectDependency) [...] ExternalProject_Add_Empty(${proj} DEPENDS ${${proj}_DEPENDENCIES})
Slicer 4.9: CMake: Module SlicerBlockSetCMakeOSXVariables not available
Since it was renamed to SlicerInitializeOSXVariables in r26982
Replace this:
include(SlicerBlockSetCMakeOSXVariables)
By this:
include(SlicerInitializeOSXVariables)
Slicer 4.9: Application: isRelease() function not available
See #Slicer_4.8:_Application:_isRelease.28.29_function_not_available_or_deprecated
Slicer 4.9: slicer.util.getNode() raises exception if node not found
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.
How to update existing code:
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.
Replace this:
n = slicer.util.getNode(nodeNameOrID)
By one of these:
If node is to be found by name:
n = slicer.mrmlScene.GetFirstNodeByName(nodeName)
If node is to be found by ID:
n = slicer.mrmlScene.GetNodeByID(nodeID)
If node is to be found by name or ID (slower, less predictable, recommended for testing only):
try: n = slicer.util.getNode(nodeNameOrID) except slicer.util.MRMLNodeNotFoundException: n = None
More information: https://github.com/Slicer/Slicer/commit/b63484af1b1b413f35396f8f7efb73e870448bd4
Slicer 4.8: Application: isRelease() function not available or deprecated
Error message similar to:
Missing/deprecated qSlicerCoreApplication::isRelease()
or
Missing/deprecated slicer.app.isRelease()
Solution:
Use qSlicerCoreApplication::releaseType() == "Stable"
Summary:
Prior to r26420, the variable Slicer_VERSION_TWEAK was used to check if a "stable release" was built. The variable value was set by updating the sources and defining the variable to an integer greater or equal to 0. In other word, if the variable evaluated to an empty string, a nighty or experimental build was being done, if it evaluated to an integer, a stable release build was being done.
The approach had few issues:
- the name of the variable was confusing
- 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.
- nightly build are also considered as release
To address this, the CMake variable Slicer_RELEASE_TYPE was introduced. As of 2017-10-04, it can be set to Experimental, Nightly or Stable with Experimental being the value hard-coded in the source.
Identifying a build as "stable" is now explicitly done by setting Slicer_RELEASE_TYPE to Stable at configure time.
Also, since the concept of release types was introduced, the function isRelease() has been removed in favor of releaseType().
References:
https://github.com/Slicer/Slicer/pull/354
Slicer Python Module: modulewidget and others removed.
Summary Python classes formerly in "slicer.moduledm", "slicer.modulelogic", "slicer.modulemrml" and "slicer.modulewidget" are now directly available in the slicer module.
See example of change here.
Rational:
See comments in commit messages referenced blow.
References:
https://github.com/Slicer/Slicer/commit/628f83fe7a6f4e0710e306bcaf7c04b9e3e5e6bd
https://github.com/Slicer/Slicer/commit/9cb5668fde1abc8f0430a91ca37fc29277ceeb4e
MRML: Slicer 4.6: Moved up vtkMRMLStorableNode in the MRML node hierarchy.
Rational:
vtkMRMLStorableNode is not a children of vtkMRMLTransformable node anymore, but directly a children of vtkMRMLNode.
This allows making a node storable without requiring it to be also transformable. It is important for several node types (color maps, tables, etc), which require separate storage node but are not transformable.
References:
- Changed introduced in r24891
Error message similar to:
/tmp/LongitudinalPETCT/MRML/vtkMRMLLongitudinalPETCTStudyNode.cxx: In member function ‘void vtkMRMLLongitudinalPETCTStudyNode::ObserveRegistrationTransform(bool)’: /tmp/LongitudinalPETCT/MRML/vtkMRMLLongitudinalPETCTStudyNode.cxx:478:28: error: ‘class vtkMRMLVolumePropertyNode’ has no member named ‘GetParentTransformNode’ && propNode->GetParentTransformNode() ^ /tmp/LongitudinalPETCT/MRML/vtkMRMLLongitudinalPETCTStudyNode.cxx:480:23: error: ‘class vtkMRMLVolumePropertyNode’ has no member named ‘SetAndObserveTransformNodeID’ propNode->SetAndObserveTransformNodeID( ^ /tmp/LongitudinalPETCT/MRML/vtkMRMLLongitudinalPETCTStudyNode.cxx:503:23: error: ‘class vtkMRMLVolumePropertyNode’ has no member named ‘SetAndObserveTransformNodeID’ propNode->SetAndObserveTransformNodeID(NULL); ^
Solution:
Removes lines and/or refactor code
MRML: Slicer 4.5: Introduction of vtkMRMLLabelMapVolumeNode
Rational:
Before vtkMRMLScalarVolumeNode was used for both scalar and label map volumes and the LabelMap custom MRML node attribute was used for distinguishing between them (0=scalar; 1=label map volume).
This made conversion between labelmap/scalar volumes very easy but made it difficult to customize behavior, display, processing of segmentation information.
Now a new vtkMRMLLabelMapVolumeNode class is used for storing segmentation information (still using vtkMRMLScalarVolume used as base class for backward compatibility; but in the future the base class may be changed to reflect that segmentation can be represented in various ways, not just as volumes).
Error message similar to:
error: ‘class vtkMRMLScalarVolumeNode’ has no member named ‘SetLabelMap’ outputVolumeNode->SetLabelMap(1); ^
Solution (part1: down cast to vtkMRMLLabelMapVolumeNode, remove call to SetLabelMap)
Replace lines like:
vtkMRMLNode* outputNode = d->OutputLabelVolumeMRMLNodeComboBox->currentNode(); vtkMRMLScalarVolumeNode* outputVolumeNode = vtkMRMLScalarVolumeNode::SafeDownCast(outputNode); [...] outputVolumeNode->SetLabelMap(1);
with:
vtkMRMLLabelMapVolumeNode* outputVolumeNode = vtkMRMLLabelMapVolumeNode::SafeDownCast(d->OutputLabelVolumeMRMLNodeComboBox->currentNode()); [...]
Solution (part2: Update UI file):
Replace lines like:
<widget class="qMRMLNodeComboBox" name="InputLabelVolumeMRMLNodeComboBox"> <property name="nodeTypes"> <stringlist> <string>vtkMRMLScalarVolumeNode</string> </stringlist> </property> [...] </widget>
with:
<widget class="qMRMLNodeComboBox" name="InputLabelVolumeMRMLNodeComboBox"> <property name="nodeTypes"> <stringlist> <string>vtkMRMLLabelMapVolumeNode</string> <------------- Update Here </stringlist> </property> [...] </widget>
Solution (part3: Update node selector configuration):
Replace lines like:
nodeSelector.setNodeTypes(QStringList("vtkMRMLScalarVolumeNode")); nodeSelector.addAttribute("vtkMRMLScalarVolumeNode", "LabelMap", "1");
with:
nodeSelector.setNodeTypes(QStringList("vtkMRMLLabelMapVolumeNode"));
References:
- https://www.slicer.org/wiki/Documentation/Labs/Segmentations#vtkMRMLLabelMapVolumeNode_integration* http://viewvc.slicer.org/viewvc.cgi/Slicer4?view=revision&revision=24291
CLI: Slicer 4.3: Add ITKFactoryRegistration library centralizing ITK IO factory registration
Rational:
Linking against ITKFactoryRegistration ensures that ITK IO factory are properly registered on all supported platforms.
Error message similar to:
Undefined symbols for architecture x86_64: "itk::itkFactoryRegistration()", referenced from: _main in ImageMakerTest.cxx.o ld: symbol(s) not found for architecture x86_64
Solution:
Replace lines like:
target_link_libraries(${CLP}Test ${CLP}Lib)
with:
target_link_libraries(${CLP}Test ${CLP}Lib ${SlicerExecutionModel_EXTRA_EXECUTABLE_TARGET_LIBRARIES})
References:
- http://viewvc.slicer.org/viewvc.cgi/Slicer4?view=revision&revision=21592
- https://issues.slicer.org/view.php?id=2813
Slicer Extension updates
Slicer 5.0: Supporting both VTK 8.0 and VTK 9.0
Transition from VTK 8.0 to VTK 9.0
See Documentation/Nightly/Developers/Tutorials/MigrationGuide#Transition_from_VTK_8.0_to_VTK_9.0
Remove building of vtk*PythonD library
With VTK >= 8.90, the vtk*Python
and vtk*Python
libraries were merged together.
To fix error messages similar to:
CMake Error at MarginCalculatorCommon/CMakeLists.txt:70 (set_target_properties): set_target_properties Can not find target to add properties to: vtkMarginCalculatorCommonPythonD
The code associated with the PythonD target could be removed to support VTK >= 8.90, or it could be conditionally excluded to support older version of VTK.
References
- Slicer c658b7b42 ENH: Add support for building against VTK 8.2.0 or 9.0.0
Slicer 5.0: ITKv4 to ITKv5
To remove support for ITKv4 and only support ITKv5. See Documentation/Nightly/Developers/Tutorials/MigrationGuide#Transition_from_ITK4_to_ITK5, it includes the following sections:
- Upgrading to ITKv5 or keep using ITKv4 GenerateThreadedData
- itkMultiThreader refactor
- SimpleFastMutexLock, FastMutexLock and MutexLock are deprecated
If completely removing support for ITKv4 and only supporting ITKv5 is not possible. It is possible to conditionally include the ITKv4 code.
For example:
#if ITK_VERSION_MAJOR >= 5 itk::ITK_THREAD_RETURN_TYPE #else ITK_THREAD_RETURN_TYPE #endif
Example of commits:
- ResampleDTIlogEuclidean
- ResampleDTIlogEuclidean@f779bf7 COMP: Use std::mutex instead of deprecated ITK implementation
- ResampleDTIlogEuclidean@ed5093b BUG: ITKv5: Fix tests updating ITK filters to use ITKv5 dynamic multi-threading
- PETTumorSegmentation
- PETTumorSegmentation PR#18 BUG: Add support for ITKv5 dynamic multithreader
- PETTumorSegmentation PR#18 COMP: Support ITKv5 refactored threading models
Slicer 5.0: Python2 to Python3
Depending on the complexity of the extension, two approaches shall be considered:
- code base common to Slicer 4.10 and Slicer 5.0. This means backward compatibility with the latest release is maintained.
- specific branch for each Slicer version (e.g master-4.10 and master)
Example of commits
- Slicer r28079 STYLE: Update python classes to follow new-style
- MultiVolumeImporter@f9917b2 STYLE: Apply lib2to3.fixes.fix_idioms to support python3
- MultiVolumeImporter@3edd1bc ENH: Support for Python3
- ShapeVariationAnalyzer@4234377 STYLE: Update python scripts for python 3.x
- PETTumorSegmentation PR#18 BUG: Add support for Python 3
Step 1
Understanding the scope of changes needed to support Python 3 can be done by:
- installing future package (see https://python-future.org/)
Slicer_DIR=/path/to/Slicer-SuperBuild/Slicer-build ${Slicer_DIR}/../python-install/bin/PythonSlicer -m pip install future
- applying all fixes
for f in `find ./ -name "*.py"`; do \ ${Slicer_DIR}/../python-install/bin/PythonSlicer \ --launch futurize --nobackups --write $f; \ done
Alternatively the future package could be installed in a regular python environment and used from there.
for f in `find ./ -name "*.py"`; do \ futurize --nobackups --write $f; \ done
This first step will apply the following transformations:
lib2to3.fixes.fix_apply lib2to3.fixes.fix_dict lib2to3.fixes.fix_except lib2to3.fixes.fix_exec lib2to3.fixes.fix_exitfunc lib2to3.fixes.fix_filter lib2to3.fixes.fix_funcattrs lib2to3.fixes.fix_getcwdu lib2to3.fixes.fix_has_key lib2to3.fixes.fix_input lib2to3.fixes.fix_intern lib2to3.fixes.fix_isinstance lib2to3.fixes.fix_itertools lib2to3.fixes.fix_itertools_imports lib2to3.fixes.fix_long lib2to3.fixes.fix_map lib2to3.fixes.fix_methodattrs lib2to3.fixes.fix_ne lib2to3.fixes.fix_next lib2to3.fixes.fix_nonzero lib2to3.fixes.fix_numliterals lib2to3.fixes.fix_operator lib2to3.fixes.fix_paren lib2to3.fixes.fix_raw_input lib2to3.fixes.fix_reduce 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 lib2to3.fixes.fix_repr lib2to3.fixes.fix_standarderror lib2to3.fixes.fix_sys_exc lib2to3.fixes.fix_throw lib2to3.fixes.fix_tuple_params lib2to3.fixes.fix_types lib2to3.fixes.fix_xreadlines lib2to3.fixes.fix_zip libfuturize.fixes.fix_absolute_import libfuturize.fixes.fix_basestring libfuturize.fixes.fix_cmp libfuturize.fixes.fix_division_safe libfuturize.fixes.fix_execfile libfuturize.fixes.fix_future_builtins libfuturize.fixes.fix_future_standard_library libfuturize.fixes.fix_future_standard_library_urllib libfuturize.fixes.fix_metaclass libfuturize.fixes.fix_next_call libfuturize.fixes.fix_object libfuturize.fixes.fix_print_with_import libfuturize.fixes.fix_raise libfuturize.fixes.fix_unicode_keep_u libfuturize.fixes.fix_xrange_with_import libpasteurize.fixes.fix_newstyle
expect these two:
lib2to3.fixes.fix_idioms lib2to3.fixes.fix_ws_comma
If the number of changes is large, you should consider applying each fixes independently.
Step 2
Not all changes applied automatically should be integrated. Most of the changes introducing imports from the future python package can simply be removed or removed after making use of try/except or check of sys.version_info[0].
- Systematic conversion of keys, values or items from dictionaries do not need to be systematically converted from dict_keys/dict_values/dict_items to list. For example, this Slicer r28077 reverted some of the automatic changes applied by the future CLI.
- Unless python classes implement the object functions next and __unicode__ specific to Python 2, nothing specific should be done and from builtins import object can be removed. Automatic removal of imports can also be automated doing:
for f in `find ./ -name "*.py"`; do \ sed -i '/from builtins import object/ d' $f; \ done
- Unless there are performances issue associated with using range in Python 2, the from builtins import range can be removed:
for f in `find ./ -name "*.py"`; do \ sed -i '/from builtins import range/ d' $f; \ done
- If there are performance issue with using range in Python 2, the following could also be done:
import sys if sys.version_info[0] == 2: range = xrange
- Use of aliases can also be avoided by removing
from future import standard_library standard_library.install_aliases()
and instead doing something like this:
try: import queue except ImportError: import Queue as queue
For a complete list aliases, see https://python-future.org/reference.html#module-future.standard_library
- Use of old_div() function can generally be avoided by using int()
Step 3
The lib2to3.fixes.fix_idioms transformation should explicitly be applied:
for f in `find ./ -name "*.py"`; do \ ${Slicer_DIR}/../python-install/bin/PythonSlicer \ --launch futurize -f lib2to3.fixes.fix_idioms --nobackups --write $f; \ done
Slicer 5.0: Python2 to Python3 (EditorEffect imports)
Error:
from EditorLib import EditorLib ImportError: cannot import name 'EditorLib'
Import of Editor classes should be updated. For example, see r28122.
Before:
import EditorLib from EditorLib.EditOptions import HelpButton from EditorLib.EditOptions import EditOptions from EditorLib import EditUtil
class TemplateKeyEffectOptions(EditorLib.LabelEffectOptions): [...]
After:
import EditorLib from EditorLib import EditOptions, HelpButton from EditorLib import EditUtil from EditorLib import LabelEffectOptions, LabelEffectTool, LabelEffectLogic, LabelEffect
class TemplateKeyEffectOptions(LabelEffectOptions): [...]
Slicer 5.0: Python packaging dicom to pydicom
The dicom python package has transitioned to become the pydicom python package. The dicom package is no longer included and all previous usages should be transitioned to use the pydicom package. See the Transition to pydicom 1.x migration guide.
Slicer 4.9: Explicit include of ExternalProject module not needed anymore
Following r26984, calling find_package(Slicer REQUIRED) and include(${Slicer_USE_FILE}) ensure the ExternalProject CMake module is included.
Note that ExternalProjectDependency CMake module is also included.
Slicer 4.9: Explicit passing of CMAKE_OSX_* variables not needed anymore
Following r26983, calling find_package(Slicer REQUIRED) and include(${Slicer_USE_FILE}) initializes CMAKE_OSX_* variables and ensures the variables CMAKE_OSX_ARCHITECTURES, CMAKE_OSX_SYSROOT and CMAKE_OSX_DEPLOYMENT_TARGET are passed to all external projects when configuring SuperBuild based extension.
Slicer 4.9: Explicit initialization of CMAKE_BUILD_TYPE not needed anymore
Following r26978, calling find_package(Slicer REQUIRED) and include(${Slicer_USE_FILE}) initializes CMAKE_BUILD_TYPE and ensures the variables CMAKE_BUILD_TYPE and CMAKE_CONFIGURATION_TYPES are passed to all external projects when configuring SuperBuild based extension.
The module SlicerInitializeBuildType is automatically included in UseSlicer CMake module.
Slicer 4.9: Subversion not required anymore
Following r27060, Subversion is not required anymore.
Example of commits
- NIRALUser/SPHARM-PDM@13373eb55: cmake: Remove unneeded SVN requirement
Slicer 4.9: Support EP_GIT_PROTOCOL and use of ExternalProject_SetIfNotDefined for setting GIT_REPOSITORY, GIT_TAG and alike
Following r26957, extension may use the following convention to define GIT_REPOSITORY and GIT_TAG, 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.
The option EP_GIT_PROTOCOL is also already set in ExternalProjectDependency module included by Slicer and its value is updated based on the <SUPERBUILD_TOPLEVEL_PROJECT>_USE_GIT_PROTOCOL option.
ExternalProject_SetIfNotDefined( ${CMAKE_PROJECT_NAME}_${proj}_GIT_REPOSITORY "${EP_GIT_PROTOCOL}://github.com/jcfr/shape4D.git" QUIET ) ExternalProject_SetIfNotDefined( ${CMAKE_PROJECT_NAME}_${proj}_GIT_TAG "12fef84ca2a56feffc59d8159bdadd2ce4a4138e" # slicersalt-2018-01-22-c74c766a4c QUIET )
See:
- https://cmake-artichoke.readthedocs.io/en/latest/ExternalProjectDependency.html#variable:EP_GIT_PROTOCOL
- https://cmake-artichoke.readthedocs.io/en/latest/ExternalProjectDependency.html#function:ExternalProject_SetIfNotDefined
Slicer 4.9: Use ExternalProject_AlwaysConfigure to force reconfigure of inner project
Following r26551, the function ExternalProject_AlwaysConfigure may be used to ensure the inner project is always reconfigured.
Using the `BUILD_ALWAYS` option supported by ExternalProject_Add will not have the intended effect.
Slicer 4.9: Specifying external projects to install in SuperBuild extension
Following r27267 and r27232, the following should be used to ensure the extension can also be bundled into a Slicer custom application. When bundled, the variable ${EXTENSION_NAME}_CPACK_INSTALL_CMAKE_PROJECTS is then used to update the application's list of project to install.
#----------------------------------------------------------------------------- set(EXTENSION_CPACK_INSTALL_CMAKE_PROJECTS) #list(APPEND EXTENSION_CPACK_INSTALL_CMAKE_PROJECTS "${Foo_DIR};Foo;RuntimeLibraries;/") set(${EXTENSION_NAME}_CPACK_INSTALL_CMAKE_PROJECTS "${EXTENSION_CPACK_INSTALL_CMAKE_PROJECTS}" CACHE STRING "List of external projects to install" FORCE) #----------------------------------------------------------------------------- list(APPEND CPACK_INSTALL_CMAKE_PROJECTS "${CMAKE_BINARY_DIR};${EXTENSION_NAME};ALL;/") list(APPEND CPACK_INSTALL_CMAKE_PROJECTS "${${EXTENSION_NAME}_CPACK_INSTALL_CMAKE_PROJECTS}") include(${Slicer_EXTENSION_GENERATE_CONFIG}) include(${Slicer_EXTENSION_CPACK})
Slicer 4.9: Generating (Extension)Config.cmake
Initially introduced in r25944, and later improved in r25991, including ${Slicer_EXTENSION_GENERATE_CONFIG} ensure a config is generated and allow an extension to import targets from another extension by using find_package(ExtensionName REQUIRED).
[...] include(${Slicer_EXTENSION_GENERATE_CONFIG}) include(${Slicer_EXTENSION_CPACK})
Slicer 4.9: Initializing <projectName>_BUILD_SLICER_EXTENSION option: Standalone vs Slicer extension build
The following snippet allows to automatically initialize <projectName>_BUILD_SLICER_EXTENSION to ON if Slicer_DIR is defined.
#----------------------------------------------------------------------------- # Standalone vs Slicer extension option #----------------------------------------------------------------------------- # This option should be named after the project name, it corresponds to the # option set to ON when the project is build by the Slicer Extension build # system. set(_default OFF) set(_reason "${PROJECT_NAME}_BUILD_SLICER_EXTENSION is ON") if(NOT DEFINED ${PROJECT_NAME}_BUILD_SLICER_EXTENSION AND DEFINED Slicer_DIR) set(_default ON) set(_reason "Slicer_DIR is SET") endif() option(${PROJECT_NAME}_BUILD_SLICER_EXTENSION "Build as a Slicer Extension" ${_default}) set(_msg "Checking if building as a Slicer extension") message(STATUS ${_msg}) if(${PROJECT_NAME}_BUILD_SLICER_EXTENSION) message(STATUS "${_msg} - yes (${_reason})") else() message(STATUS "${_msg} - no (${PROJECT_NAME}_BUILD_SLICER_EXTENSION is OFF)") endif() mark_as_superbuild(${PROJECT_NAME}_BUILD_SLICER_EXTENSION:BOOL)
Obsolete Code Removal
This section documents suggested code changes after removing support for a particular features. Each category has a short description, code snippets, a suggested upgrade path, and references to relevant commits.
Qt>=5.0.0: Remove obsolete code supporting Qt4 plugin infrastructure (C++)
These changes apply to Qt loadable modules and designer plugins
qSlicerNAMEModuleWidgetsPlugin.h
Before:
#include "vtkSlicerConfigure.h" // For Slicer_HAVE_QT5 // Qt includes #ifdef Slicer_HAVE_QT5 #include <QtUiPlugin/QDesignerCustomWidgetCollectionInterface> #else #include <QDesignerCustomWidgetCollectionInterface> #endif [...] class Q_SLICER_MODULE_SUBJECTHIERARCHY_WIDGETS_PLUGINS_EXPORT qSlicerSubjectHierarchyModuleWidgetsPlugin : public QObject , public QDesignerCustomWidgetCollectionInterface { Q_OBJECT #ifdef Slicer_HAVE_QT5 Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QDesignerCustomWidgetCollectionInterface") #endif Q_INTERFACES(QDesignerCustomWidgetCollectionInterface);
After:
// Qt includes #include <QtUiPlugin/QDesignerCustomWidgetCollectionInterface> [...] class Q_SLICER_MODULE_SUBJECTHIERARCHY_WIDGETS_PLUGINS_EXPORT qSlicerSubjectHierarchyModuleWidgetsPlugin : public QObject , public QDesignerCustomWidgetCollectionInterface { Q_OBJECT Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QDesignerCustomWidgetCollectionInterface") Q_INTERFACES(QDesignerCustomWidgetCollectionInterface);
qSlicerNAMEModule.h
Before:
class Q_SLICER_QTMODULES_SUBJECTHIERARCHY_EXPORT qSlicerSubjectHierarchyModule : public qSlicerLoadableModule { Q_OBJECT #ifdef Slicer_HAVE_QT5 Q_PLUGIN_METADATA(IID "org.slicer.modules.loadable.qSlicerLoadableModule/1.0"); #endif
After:
class Q_SLICER_QTMODULES_SUBJECTHIERARCHY_EXPORT qSlicerSubjectHierarchyModule : public qSlicerLoadableModule { Q_OBJECT Q_PLUGIN_METADATA(IID "org.slicer.modules.loadable.qSlicerLoadableModule/1.0");
qSlicerNAMEModuleWidgetsAbstractPlugin.h
Before:
#include <QtGlobal> #if(QT_VERSION < QT_VERSION_CHECKS(5, 0, 0)) #include <QDesignerCustomWidgetInterface> #else #include <QtUiPlugin/QDesignerCustomWidgetInterface> #endif [...] class Q_SLICER_MODULE_SEGMENTATIONS_WIDGETS_PLUGINS_EXPORT qSlicerSegmentationsModuleWidgetsAbstractPlugin : public QDesignerCustomWidgetInterface { #if (QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)) Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QDesignerCustomWidgetInterface") #endif Q_INTERFACES(QDesignerCustomWidgetInterface); }
After:
#include <QtUiPlugin/QDesignerCustomWidgetInterface> [...] class Q_SLICER_MODULE_SEGMENTATIONS_WIDGETS_PLUGINS_EXPORT qSlicerSegmentationsModuleWidgetsAbstractPlugin : public QDesignerCustomWidgetInterface { Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QDesignerCustomWidgetInterface") Q_INTERFACES(QDesignerCustomWidgetInterface); }
qSlicerNAMEModule.cxx and qSlicerNAMEModuleWidgetsAbstractPlugin.cxx
Remove:
#if (QT_VERSION < QT_VERSION_CHECK(5, 0, 0)) #include <QtPlugin> Q_EXPORT_PLUGIN2(customwidgetplugin, qSlicerSegmentationsModuleWidgetsPlugin); #endif
Qt>=5.0.0: Simpler use of QHeaderView::setSectionResizeMode
This migration guide entry is obsolete: Documentation/Nightly/Developers/Tutorials/MigrationGuide#Qt5:_Fix_error:_.E2.80.98class_QHeaderView.E2.80.99_has_no_member_named_.E2.80.98setResizeMode.E2.80.99
Solution for Cpp
Before:
#if (QT_VERSION < QT_VERSION_CHECK(5, 0, 0)) this->SceneViewTableWidget->horizontalHeader()->setResizeMode(QHeaderView::Stretch); this->SceneViewTableWidget->horizontalHeader()->setResizeMode(QHeaderView::ResizeToContents); this->SceneViewTableWidget->horizontalHeader()->setResizeMode(SCENE_VIEW_DESCRIPTION_COLUMN, QHeaderView::Stretch); #else this->SceneViewTableWidget->horizontalHeader()->setSectionResizeMode(QHeaderView::Stretch); this->SceneViewTableWidget->horizontalHeader()->setSectionResizeMode(QHeaderView::ResizeToContents); this->SceneViewTableWidget->horizontalHeader()->setSectionResizeMode(SCENE_VIEW_DESCRIPTION_COLUMN, QHeaderView::Stretch); #endif
After:
this->SceneViewTableWidget->horizontalHeader()->setSectionResizeMode(QHeaderView::Stretch); this->SceneViewTableWidget->horizontalHeader()->setSectionResizeMode(QHeaderView::ResizeToContents); this->SceneViewTableWidget->horizontalHeader()->setSectionResizeMode(SCENE_VIEW_DESCRIPTION_COLUMN, QHeaderView::Stretch);
Solution for Python
Before:
def _setSectionResizeMode(header, *args, **kwargs): if version.parse(qt.Qt.qVersion()) < version.parse("5.0.0"): header.setResizeMode(*args, **kwargs) else: header.setSectionResizeMode(*args, **kwargs) [...] _setSectionResizeMode(self.horizontalHeader(), 0, qt.QHeaderView.Stretch)
After:
self.horizontalHeader().setSectionResizeMode(0, qt.QHeaderView.Stretch)
C++11: Update source code to use nullptr
C++11 introduced the keyword nullptr allowing to explicitly identify a pointer of type std::nullptr_t. It avoids ambiguous function overloads. See also https://stackoverflow.com/questions/20509734/null-vs-nullptr-why-was-it-replaced
Prior to supporting only C++11, 0 and NULL integral type were used to check for pointer value, and macro like Q_NULLPTR and ITK_NULLPTR macros were used to ensure usage of nullptr when C++11 support was enabled.
Updating the code base to use nullptr is done applying these methods:
- clang-tidy for updating source code being explicitly compiled in the project.
- regular expression for replacing occurrences of NULL in source code not being compiled (e.g conditionally compiled code, templated headers, comments)
- replacement of occurences of VTK_NULLPTR, ITK_NULLPTR, Q_NULLPTR with nullptr
Using clang-tidy
This will iteratively check files for updates. It takes a while. The sources are only modified at the end of the process, so you could cancel anytime before with no consequences.
- Compile Slicer with the CMake option -DCMAKE_EXPORT_COMPILE_COMMANDS:BOOL=ON to generate the file compile_commands.json at configuration time.
- Run clang-tidy on the generated compile_commands.json (in the build folder: Slicer-build)
run-clang-tidy.py -header-filter='.*' -checks='-*,modernize-use-nullptr' -fix
clang-tidy is a tool from clang. In Archlinux: sudo pacman -S clang, in other distros might be clang-extra-tools or similar.
clang-tidy will substitute NULL and 0 for nullptr when appropriate. It is an iterative process, so it may take time. Also, it replaces all the compiled code, including external dependencies. It is recommended to do a fresh build with the modified code, for testing it without modifications in any external repo.
Using regex for replacing NULL in comments and not-compiled code
After clang-tidy has modified the compiled code with the help of compile_commands.json, there will still be ignored instances of NULL and 0 in some headers, and comments.
Manually replace with the following regex to change those.
1) Change NULL to nullptr except when there is a quote or an underscore in front of it. And there is not a quote after.
ack --type-add=cpp:ext:hxx,hpp,txx,tpp,cxx,cpp,c,cc --cpp "NULL" -l --print0 | xargs -0 -n 1 sed -i 's/\([^\"\_]\)NULL\([^\"\_]\)/\1nullptr\2/g'
2) The same than before but match end of line NULL
ack --type-add=cpp:ext:hxx,hpp,txx,tpp,cxx,cpp,c,cc --cpp "NULL" -l --print0 | xargs -0 -n 1 sed -i 's/\([^\"\_]\)NULL$/\1nullptr/g'
Replace ITK_NULLPTR, VTK_NULLPTR, Q_NULLPTR
It was adapted from the ITK script in ReplaceITK_NULLPTRMacroNames.sh
- ITK_NULLPTR:
git grep -l "ITK_NULLPTR" | fgrep -v CMakeLists.txt |fgrep -v .cmake | xargs sed -i '' -e "s/ITK_NULLPTR/nullptr/g"
- VTK_NULLPTR:
git grep -l "VTK_NULLPTR" | fgrep -v CMakeLists.txt |fgrep -v .cmake | xargs sed -i '' -e "s/VTK_NULLPTR/nullptr/g"
- Q_NULLPTR:
git grep -l "Q_NULLPTR" | fgrep -v CMakeLists.txt |fgrep -v .cmake | xargs sed -i '' -e "s/Q_NULLPTR/nullptr/g"
C++11: Update source code to use override
From: https://clang.llvm.org/extra/clang-tidy/checks/modernize-use-override.html
Adds override (introduced in C++11) to overridden virtual functions and removes virtual from those functions as it is not required.
virtual on non base class implementations was used to help indicate to the user that a function was virtual. C++ compilers did not use the presence of this to signify an overriden function.
In C++ 11 override and final keywords were introduced to allow overridden functions to be marked appropriately. Their presence allows compilers to verify that an overridden function correctly overrides a base class implementation.
This can be useful as compilers can generate a compile time error when:
- The base class implementation function signature changes.
- The user has not created the override with the correct signature.
First we replace ITK_OVERRIDE and VTK_OVERRIDE for override:
Replace ITK_OVERRIDE and VTK_OVERRIDE
For an example of commit message, see r28022
The regexp involving ITK was extracted from the ReplaceITK_OVERRIDEMacroNames.sh script in ITK.
- ITK_OVERRIDE
git grep -l "ITK_OVERRIDE" | fgrep -v CMakeLists.txt |fgrep -v .cmake | xargs sed -i '' -e "s/ITK_OVERRIDE/override/g"
- VTK_OVERRIDE
git grep -l "VTK_OVERRIDE" | fgrep -v CMakeLists.txt |fgrep -v .cmake | xargs sed -i '' -e "s/VTK_OVERRIDE/override/g"
Automatic update: Using clang-tidy
Note: Running clang-tidy -fix in parallel might cause problems, like appending multiple keywords to same function (override). It can be fixed with a grep to remove duplicates.
This will iteratively check files for updates. It takes a while. The sources are only modified at the end of the process, so you could cancel anytime before with no consequences.
Compile Slicer with the CMake option -DCMAKE_EXPORT_COMPILE_COMMANDS:BOOL=ON to generate the file compile_commands.json at configuration time. Run clang-tidy on the generated compile_commands.json (in the build folder: Slicer-build)
run-clang-tidy.py -checks=-*,modernize-use-override -header-filter=.* -fix
Manual fix
Multiple override are added to the same method. For example:
void PrintSelf(ostream& os, vtkIndent indent) override override override;
We clean them with (15 here is just a high enough number):
for n in `seq 1 15`; do ack "override override" -l --print0 | xargs -0 -n 1 sed -i \ 's/override override/override/g' done
When all of them are fixed, you should see in the terminal:
sed: no input files
C++11: Update source code to use = delete
Instead of:
vtkImageRectangularSource(const vtkImageRectangularSource&); /// Not implemented. void operator=(const vtkImageRectangularSource&); /// Not implemented.
Replace for:
vtkImageRectangularSource(const vtkImageRectangularSource&) = delete; void operator=(const vtkImageRectangularSource&) = delete;
Same approach than before, use the compile_commands.json and clang-tidy in Slicer-build.
run-clang-tidy.py -checks=-*,modernize-use-equals-delete -header-filter=.* -fix
Because we run it in parallel, multiple occurrences of = delete might have been added. Fix them with:
for n in `seq 1 15`; do ack "= delete = delete" -l --print0 | xargs -0 -n 1 sed -i \ 's/= delete = delete/= delete/g' done
And also delete comment /// Not implemented, as now it is clear with = delete.
ack "delete" -l --print0 | xargs -0 -n 1 sed -E -i 's@delete;\s*\/*\s*[nN]ot [iI]mplemented\.?@delete;@g' ack "delete" -l --print0 | xargs -0 -n 1 sed -E -i 's@delete;\s*\/*\s*purposely [nN]ot [iI]mplemented\.?@delete;@g' ack "delete" -l --print0 | xargs -0 -n 1 sed -E -i 's@delete ITK\_DELETED\_FUNCTION;@delete;@g'
Next step in this area would be to move these declarations to the public interface (currently they are private).
References:
https://clang.llvm.org/extra/clang-tidy/checks/modernize-use-equals-delete.html
C++11: Update source code to use = default
Instead of:
virtual ~DataRequest(){}
Replace for:
virtual ~DataRequest() = default;
Same approach than before, use the compile_commands.json and clang-tidy in Slicer-build.
run-clang-tidy.py -checks=-*,modernize-use-equals-default -header-filter=.* -fix
Because we run it in parallel, multiple colons ;; might have been added. Fix them with:
for n in `seq 1 15`; do ack "default" -l --print0 | xargs -0 -n 1 sed -i 's/= default;;/= default;/g' done
Add an empty space before = default if there is none:
ack "default" -l --print0 | xargs -0 -n 1 sed -E -i 's/([^\s])= default;/\1 = default;/g'
References:
https://clang.llvm.org/extra/clang-tidy/checks/modernize-use-equals-default.html
C++11: Use default member initialization
Instead of:
// vtkMRMLFooNode.h class VTK_MRML_EXPORT vtkMRMLFooNode : public vtkMRMLAbstractBarNode { [...] bool DoSomething; int InteractionMode; } // vtkMRMLFooNode.cxx vtkMRMLFooNode::vtkMRMLFooNode() : DoSomething(true) , FooMode(FooModeAwesome) { [...] }
Replace with:
// vtkMRMLFooNode.h class VTK_MRML_EXPORT vtkMRMLFooNode : public vtkMRMLAbstractBarNode { [...] bool DoSomething{true}; int FooMode{FooModeAwesome}; }; // vtkMRMLFooNode.cxx vtkMRMLFooNode::vtkMRMLFooNode() { [...] } // or this if constructor is empty after removing old-style default initialization // vtkMRMLFooNode.cxx vtkMRMLFooNode::vtkMRMLFooNode() = default;
Same approach than before, use the compile_commands.json and clang-tidy in Slicer-build.
run-clang-tidy.py -checks=-*,modernize-use-default-member-init -header-filter=.* -fix
References:
https://clang.llvm.org/extra/clang-tidy/checks/modernize-use-default-member-init.html
Transition from ITK4 to ITK5
This section lists categories of code changes necessary to build Slicer with ITK 5. Each category has a short description, an example error message, a suggested upgrade path, and references to relevant commits.
Referencing this list might be helpful if Slicer extensions requiring updates to be compatible with ITK 5.
Make also sure to read through the ITK 5 migration guide.
Utilities/ITKv5Preparation directory contains bash scripts which have been used to update ITK to version 5. These scripts can assist with updating external code bases to ITK 5 content and style.
Upgrading to ITKv5 or keep using ITKv4 GenerateThreadedData
If the ThreadedGenerateData does not use threadId, change the name of the function to DynamicThreadedGenerateData Replace:
void ThreadedGenerateData( const OutputRegionType& threadRegion, ThreadIdType threadId )
with:
void DynamicThreadedGenerateData( const OutputRegionType& threadRegion )
If you want to keep using the ITKv4 thread system (with ThreadedGenerateData), add to the constructor of your ITK class:
this->DynamicMultiThreadingOff();
itkMultiThreader refactor
Replace lines like:
#include <itkMultiThreader.h> itk::MultiThreader::Pointer ProcessingThreader; ITK_THREAD_RETURN_TYPE ITK_THREAD_RETURN_VALUE SetNumberOfThreads()
with:
#include <itkPlatformMultiThreader.h> itk::PlatformMultiThreader::Pointer ProcessingThreader; itk::ITK_THREAD_RETURN_TYPE itk::ITK_THREAD_RETURN_DEFAULT_VALUE SetNumberOfWorkUnits()
SimpleFastMutexLock, FastMutexLock and MutexLock are deprecated
Instead use std::mutex available in C++11
Replace:
#include "itkSimpleFastMutexLock.h" #include "itkFastMutexLock.h" #include "itkMutexLock.h" SimpleFastMutexLock FastMutexLock MutexLock m_Lock->Lock(); m_Lock->Unlock();
with:
#include <mutex> std::mutex m_Lock.lock(); m_Lock.unlock();
Transition from VTK 8.0 to VTK 9.0
This section lists categories of code changes necessary to build Slicer with VTK 9.0. Each category has a short description, an example error message, a suggested upgrade path, and references to relevant commits.
VTK9: Use const vtkIdType*
To fix error messages similar to:
/tmp/SPHARM-PDM/Modules/CLI/MetaMeshTools/vtkPolyDataToitkMesh.cxx:82:70: error: invalid initialization of non-const reference of type ‘const vtkIdType*& {aka const long long int*&}’ from an rvalue of type ‘const vtkIdType* {aka const long long int*}’ while( triangleStrips->GetNextCell( numberOfCellPoints, cellPoints ) ) ^ [...] /path/to/Slicer-Release/VTK/Common/DataModel/vtkCellArray.h:1497:12: note: initializing argument 2 of ‘int vtkCellArray::GetNextCell(vtkIdType&, const vtkIdType*&)’ inline int vtkCellArray::GetNextCell(vtkIdType& npts, vtkIdType const*& pts) VTK_SIZEHINT(pts, npts) ^ /tmp/SPHARM-PDM/Modules/CLI/MetaMeshTools/vtkPolyDataToitkMesh.cxx:91:64: error: invalid initialization of non-const reference of type ‘const vtkIdType*& {aka const long long int*&}’ from an rvalue of type ‘const vtkIdType* {aka const long long int*}’ while( polygons->GetNextCell( numberOfCellPoints, cellPoints ) ) ^ ^
Replace code like this:
vtkIdType* cellPoints;
By this:
#if VTK_MAJOR_VERSION >= 9 || (VTK_MAJOR_VERSION >= 8 && VTK_MINOR_VERSION >= 90) const vtkIdType* cellPoints; #else vtkIdType* cellPoints; #endif
References:
VTK9: Add missing std namespace identifier
To fix error messages similar to:
/usr/include/c++/5/iosfwd:162:34: note: ‘std::ifstream’ /tmp/SPHARM-PDM/Modules/CLI/MetaMeshTools/MeshMath.cxx:4082:5: error: ‘ofstream’ was not declared in this scope ofstream output; ^
/usr/include/c++/5/iosfwd:162:34: note: ‘std::ifstream’ /tmp/SPHARM-PDM/Modules/CLI/MetaMeshTools/MeshMath.cxx:4117:5: error: ‘ofstream’ was not declared in this scope ofstream output; ^
Replace code like this:
ifstream input; ofstream output;
By this:
std::ifstream input; std::ofstream output;
References:
Transition from VTK7-Qt4 to VTK8-Qt5
This section lists categories of code changes necessary to build and run Slicer with VTK 8.0 and Qt5. Each category has a short description, a suggested upgrade path, and references to relevant commits (TBD once merged).
Slicer SuperBuild Extension: Enable C++11 in external projects
SuperBuild extensions may have to enable C++11 for their external projects. Add the following lines to CMAKE_CACHE_ARGS in ExternalProject_Add
-DCMAKE_CXX_STANDARD:STRING=${CMAKE_CXX_STANDARD} -DCMAKE_CXX_STANDARD_REQUIRED:BOOL=${CMAKE_CXX_STANDARD_REQUIRED} -DCMAKE_CXX_EXTENSIONS:BOOL=${CMAKE_CXX_EXTENSIONS}
Suggested commit message:
COMP: Enable C++11 in external projects See https://www.slicer.org/wiki/Documentation/Nightly/Developers/Tutorials/MigrationGuide/VTK7-Qt4-to-VTK8-Qt5#Slicer_SuperBuild_Extension:_Enable_C.2B.2B11_in_external_projects
Error message similar to:
/path/to/Slicer-build/ITKv4-build/Modules/Core/Common/itkConfigure.h:59:6: warning: #warning "WARNING: The current project is configured to use a C++ standard version older than the C++ standard used for this build of ITK" [-Wcpp] #warning "WARNING: The current project is configured to use a C++ standard version older than the C++ standard used for this build of ITK" ^
Qt5: Update loadable modules to use new plugin macros
In Qt5, the Q_EXPORT_PLUGIN
, Q_EXPORT_PLUGIN2
macros have been deprecated in favor of the new Q_PLUGIN_METADATA
macro.
Error message similar to:
error: static assertion failed: Old plugin system used
Suggested commit message:
COMP: Qt5: Fix error: static assertion failed: Old plugin system used See https://www.slicer.org/wiki/Documentation/Nightly/Developers/Tutorials/MigrationGuide/VTK7-Qt4-to-VTK8-Qt5#Qt5:_Update_loadable_modules_to_use_new_plugin_macros
Solution (part 1):
In qSlicer<NameOfModule>Module.h, replace lines like:
Q_OBJECT Q_INTERFACES(qSlicerLoadableModule);
with:
Q_OBJECT #ifdef Slicer_HAVE_QT5 Q_PLUGIN_METADATA(IID "org.slicer.modules.loadable.qSlicerLoadableModule/1.0"); #endif Q_INTERFACES(qSlicerLoadableModule);
Solution (part 2):
In qSlicer<NameOfModule>Module.cxx, Replace lines like:
Q_EXPORT_PLUGIN2(qSlicer<NameOfModule>Module, qSlicer<NameOfModule>Module);
with:
#if (QT_VERSION < QT_VERSION_CHECK(5, 0, 0)) #include <QtPlugin> Q_EXPORT_PLUGIN2(qSlicer<NameOfModule>Module, qSlicer<NameOfModule>Module); #endif
References:
Qt5: Update DesignerPlugin to use QtUiPlugin/QDesignerCustomWidgetInterface
In Qt5, the QtDesigner/QDesignerCustomWidgetInterface
header have been deprecated in favor of the new QtUiPlugin/QDesignerCustomWidgetInterface
header.
Error message similar to:
/path/to/include/QtDesigner/QDesignerCustomWidgetInterface:4:4: warning: #warning Header <QtDesigner/QDesignerCustomWidgetInterface> is deprecated. Please include <QtUiPlugin/QDesignerCustomWidgetInterface> instead. [-Wcpp] # warning Header <QtDesigner/QDesignerCustomWidgetInterface> is deprecated. Please include <QtUiPlugin/QDesignerCustomWidgetInterface> instead. ^
Suggested commit message:
COMP: Update designer plugin to support Qt5 See https://www.slicer.org/wiki/Documentation/Nightly/Developers/Tutorials/MigrationGuide/VTK7-Qt4-to-VTK8-Qt5#Qt5:_Update_DesignerPlugin_to_use_QtUiPlugin.2FQDesignerCustomWidgetInterface
Solution (part 1):
In Widgets/DesignerPlugins/qSlicer<NameOfModule>ModuleWidgetsAbstractPlugin.h, replace lines like:
#include <QDesignerCustomWidgetInterface>
with:
#include <QtGlobal> #if (QT_VERSION < QT_VERSION_CHECK(5, 0, 0)) #include <QDesignerCustomWidgetInterface> #else #include <QtUiPlugin/QDesignerCustomWidgetInterface> #endif
Solution (part 2):
In Widgets/DesignerPlugins/qSlicer<NameOfModule>ModuleWidgetsAbstractPlugin.h, replace lines like:
Q_INTERFACES(QDesignerCustomWidgetInterface);
with:
#if (QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)) Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QDesignerCustomWidgetInterface") #endif Q_INTERFACES(QDesignerCustomWidgetInterface);
Qt5: Update DesignerPlugin to use QtUiPlugin/QDesignerCustomWidgetCollectionInterface
Error message similar to:
/path/to/5.9.1/gcc_64/include/QtDesigner/QDesignerCustomWidgetCollectionInterface:4:4: warning: #warning Header <QtDesigner/QDesignerCustomWidgetCollectionInterface> is deprecated. Please include <QtUiPlugin/QDesignerCustomWidgetCollectionInterface> instead. [-Wcpp] # warning Header <QtDesigner/QDesignerCustomWidgetCollectionInterface> is deprecated. Please include <QtUiPlugin/QDesignerCustomWidgetCollectionInterface> instead. ^
Solution (part 1):
In Widgets/DesignerPlugins/qSlicer<NameOfModule>ModuleWidgetsPlugin.h, replace lines like:
#include <QDesignerCustomWidgetCollectionInterface>
with:
#include "vtkSlicerConfigure.h" // For Slicer_HAVE_QT5 // Qt includes #ifdef Slicer_HAVE_QT5 #include <QtUiPlugin/QDesignerCustomWidgetCollectionInterface> #else #include <QDesignerCustomWidgetCollectionInterface> #endif
Solution (part 2):
In Widgets/DesignerPlugins/qSlicer<NameOfModule>ModuleWidgetsPlugin.h, replace lines like:
Q_INTERFACES(QDesignerCustomWidgetCollectionInterface);
with:
#if (QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)) Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QDesignerCustomWidgetInterface") #endif Q_INTERFACES(QDesignerCustomWidgetCollectionInterface);
Qt5: any use of QWebKit needs to switch to QWebEngine
TBD
Qt5: QVTKOpenGLWidget
When using Qt5, QVTKOpenGLWidget should be used in place of QVTKGLWidget.
To ensure that QVTKOpenGLWidget receives a properly configured OpenGL context it's necessary to call QSurfaceFormat::setDefaultFormat() before constructing the QApplication instance. QVTKOpenGLWidget::defaultFormat() supplies a suitable format, although it's recommended to disable multisampling for full compatibility with advanced rendering techniques. See http://doc.qt.io/qt-5/qopenglwidget.html.
Error message similar to:
error: no matching function for call to ‘QVTKWidget::QVTKWidget(QWidget*&)’ scalarsToColorsView = new QVTKWidget(ctkVTKDiscretizableColorTransferWidget); ^
Solution (part 1):
In NameOfClass.h file, replace lines like:
#include <QVTKWidget.h>
with:
#if CTK_USE_QVTKOPENGLWIDGET #include <QVTKOpenGLWidget.h> #else #include <QVTKWidget.h> #endif
Solution (part 2):
In NameOfClass.h file, replace lines like:
QVTKWidget* View;
with:
#if CTK_USE_QVTKOPENGLWIDGET QVTKOpenGLWidget* View; #else QVTKWidget* View; #endif
Solution (part 3):
In NameOfClass.cpp file, replace lines like:
this->View = new QVTKWidget;
with:
#if CTK_USE_QVTKOPENGLWIDGET this->View = new QVTKOpenGLWidget; #else this->View = new QVTKWidget; #endif
Qt5: Fix error: 'class QString' has no member named 'toAscii'
Replace call to toAscii().data()
with toLatin1().data()
References:
Qt5: Fix error: ‘class QHeaderView’ has no member named ‘setResizeMode’
Error message similar to:
error: ‘class QHeaderView’ has no member named ‘setResizeMode’ headerView->setResizeMode(FileColumn, QHeaderView::Stretch);
Solution:
Replace lines like:
headerView->setResizeMode(FileColumn, QHeaderView::Stretch);
with:
#if (QT_VERSION < QT_VERSION_CHECK(5, 0, 0)) headerView->setResizeMode(FileColumn, QHeaderView::Stretch); #else headerView->setSectionResizeMode(FileColumn, QHeaderView::Stretch); #endif
Solution for Python:
from packaging import version ... def _setSectionResizeMode(header, *args, **kwargs): """ To be compatible with Qt4 and Qt5 """ if version.parse(qt.Qt.qVersion()) < version.parse("5.0.0"): header.setResizeMode(*args, **kwargs) else: header.setSectionResizeMode(*args, **kwargs) ... _setSectionResizeMode(horizontalHeader, 1, qt.QHeaderView.ResizeToContents)
VTK8: Use hierarchy files for VTK Python wrapping
In VTK8 it's necessary to generate hierarchy files for proper wrapping VTK classes in Python. Without the information provided by the hierarchy files, the Python wrapping tool lacks complete information about classes and types. In this case, the generated classes contain methods that shouldn't be wrapped and fail to compile, and include references to types such as vtkTypeBool. Once the hierarchy files are generated and provided to the Python wrapping tool, the generated classes compile and typedefs like vtkTypeBool are correctly resolved.
Once the VTK8 changes are merged, generating hierarchy files is handled by https://github.com/Slicer/Slicer/blob/master/CMake/vtkMacroKitPythonWrap.cmake.
References:
VTK8: Use of vtkTypeMacro requires to use the correct base class
Error message similar to:
error: expected unqualified-id before 'protected'
This error is usually a symptom of an incorrect base class when using vtkTypeMacro.
Solution:
Assuming the class vtkIGTLToMRMLPoint derives from vtkIGTLToMRMLBase,
Replace lines like:
vtkTypeMacro(vtkIGTLToMRMLPoint,vtkObject);
with:
vtkTypeMacro(vtkIGTLToMRMLPoint,vtkIGTLToMRMLBase);
References:
VTK8: Copy constructor and equal operator should be disabled
Error message similar to:
error: use of deleted function 'vtkMyClass::vtkMyClass(const vtkMyClass&)'
This error is usually a symptom of not disabling the copy constructor and equal operator.
Solution:
Replace lines like:
protected: vtkMyClass(); ~vtkMyClass();
with:
protected: vtkMyClass(); ~vtkMyClass(); vtkMyClass(const vtkMyClass&); void operator=(const vtkMyClass&);
VTK8: Call InitializeObjectBase() in vtkObject New() methods
In VTK8 it's necessary for vtkObject New() methods to call InitializeObjectBase() on the new object for proper tracking with vtkDebugLeaks. The standard macros (vtkStandardNewMacro, vtkObjectFactoryNewMacro) handle this. For those classes that don't use the macros, add a call to InitializeObjectBase() immediately after constructing the object by new vtkXXX().
Additionally, vtkObjectFactory::CreateInstance() now doesn't register the class name with vtkDebugLeaks if the factory fails to create the object. Therefore, it's no longer necessary to unregister the class name with vtkDebugLeaks. Remove calls to vtkDebugLeaks::DestructClass(className) following vtkObjectFactory::CreateInstance().
To support both VTK8 and earlier versions of VTK, wrap these changes in preprocessor checks for whether VTK_HAS_INITIALIZE_OBJECT_BASE is defined.
References:
- https://github.com/Kitware/VTK/commit/e5c793dbdf87e838bb2b60c6a5905ced0e5548f9
- http://public.kitware.com/pipermail/vtk-developers/2016-September/034332.html
VTK8: Add C++11 keywords
VTK8 requires C++11. Subclasses of VTK classes must mark overridden methods with VTK_OVERRIDE.
VTK8: vtkWindowToImageFilter::SetMagnification() is deprecated
VTK8.1 deprecated vtkWindowToImageFilter::SetMagnification() and vtkWindowToImageFilter::GetMagnification(). Replace calls to those methods with SetScale() and GetScale(). See https://github.com/Kitware/VTK/commit/af0a95fa7dd4e25ef869a0bc6077e547f18baa29.
VTK8: vtkInstantiator is deprecated
VTK8.1 deprecated vtkInstantiator. See https://github.com/Kitware/VTK/commit/11bb6a4d395e877847355a63de2e2e8f8d9e1d91
Error message similar to:
/path/to/SlicerIGT-Release/ToolWatchdog/MRMLDM/WatchdogInstantiator.cxx:10:1: error: expected constructor, destructor, or type conversion before ‘void’ void WatchdogInstantiator::ClassInitialize() ^ /path/to/SlicerIGT-Release/ToolWatchdog/MRMLDM/WatchdogInstantiator.cxx: In static member function ‘static void WatchdogInstantiator::ClassFinalize()’: /path/to/SlicerIGT-Release/ToolWatchdog/MRMLDM/WatchdogInstantiator.cxx:21:3: error: ‘vtkInstantiator’ has not been declared vtkInstantiator::UnRegisterInstantiator("vtkMRMLWatchdogDisplayableManager", vtkInstantiatorvtkMRMLWatchdogDisplayableManagerNew); ^ /path/to/SlicerIGT-Release/ToolWatchdog/MRMLDM/WatchdogInstantiator.cxx:21:80: error: ‘vtkInstantiatorvtkMRMLWatchdogDisplayableManagerNew’ was not declared in this scope vtkInstantiator::UnRegisterInstantiator("vtkMRMLWatchdogDisplayableManager", vtkInstantiatorvtkMRMLWatchdogDisplayableManagerNew); ^
Solution (part 1):
Update associated CMakeLists.txt replacing lines like:
set(VTK_USE_INSTANTIATOR_NEW 1) if(${VTK_VERSION_MAJOR} GREATER 5) include(${VTK_CMAKE_DIR}/vtkMakeInstantiator.cmake) endif() VTK_MAKE_INSTANTIATOR3("${MODULE_NAME}Instantiator" displayable_manager_instantiator_SRCS "${displayable_manager_SRCS}" "${${KIT}_EXPORT_DIRECTIVE}" ${CMAKE_CURRENT_BINARY_DIR} "${KIT}Export.h" )
with:
SlicerConfigureDisplayableManagerObjectFactory( TARGET_NAME ${KIT} SRCS "${displayable_manager_SRCS}" EXPORT_MACRO "${${KIT}_EXPORT_DIRECTIVE}" EXPORT_HEADER "${KIT}Export.h" OUTPUT_SRCS_VAR displayable_manager_instantiator_SRCS )
Solution (part 2):
Update associated qSlicer<NameOfModule>Module.h (or any test requiring displayable manager) adding lines like:
// DisplayableManager initialization #include <vtkAutoInit.h> VTK_MODULE_INIT(vtkSlicer<NameOfModule>ModuleMRMLDisplayableManager)
Solution (part 2 - maintaining backward compatibility with Slicer 4.8):
#include <vtkSlicerVersionConfigure.h> // For Slicer_VERSION_MAJOR, Slicer_VERSION_MINOR [...] // DisplayableManager initialization #if Slicer_VERSION_MAJOR == 4 && Slicer_VERSION_MINOR >= 9 #include <vtkAutoInit.h> VTK_MODULE_INIT(vtkSlicer<NameOfModule>ModuleMRMLDisplayableManager) #endif
For example, see https://github.com/SlicerIGT/SlicerIGT/pull/155/commits/2f866ea8872435b9a3a7382dd0549231da00406f
VTK 8.2: Signature of vtkFSLookupTable::MapValue updated
The signature of the function was updated in Kitware/VTK@43f6ee3
Error message similar to:
error: invalid conversion from ‘const unsigned char*’ to ‘unsigned char*’ [-fpermissive]
overriding virtual function return type differs and is not covariant from 'vtkLookupTable::MapValue'
Solution:
Update code to use `const` keyword. For example, see r26708
Slicer scripted module initialization steps after application startup
In Slicer 4.8 and earlier versions, those module initialization steps that required application startup completed, often were called using a singleshot timer, because timer events were only processed after the application startup was completed. In Slicer 4.9, some event processing is performed before the application startup is completed, therefore instead of relying on a timer, the application's startupCompleted() signal must be used.
Replace line like:
qt.QTimer.singleShot(0, self.myAdditionalInitializationSteps)
with:
slicer.app.connect("startupCompleted()", self.myAdditionalInitializationSteps)
Transition from VTK6 to VTK7
This section lists categories of code changes necessary to build Slicer with VTK 7.1. Each category has a short description, an example error message, a suggested upgrade path, and references to relevant commits.
Referencing this list might be helpful if Slicer extensions require updates to be compatible with VTK 7.1.
Deprecated the vtkStreamer class hierarchy
VTK has deprecated the vtkStreamer class hierarchy.
Error message similar to:
'VTK_INTEGRATE_FORWARD' : undeclared identifier
Solution:
Replace lines like:
#include "vtkStreamer.h"
with:
#include "vtkHyperStreamline.h"
References:
- https://github.com/Slicer/Slicer/commit/a78fa0dc33d7c3822bff14b0e2bc403cf6ee9b31 (COMP: Update #include of deprecated vtkStreamer.h)
- https://github.com/Kitware/VTK/commit/c9f7a5e (vtkStreamer: deprecate the class hierarchy)
Deprecated vtkMatrix4x4::operator[] method
VTK has deprecated the vtkMatrix4x4::operator[] method.
Error message similar to:
binary '[' : 'vtkMatrix4x4' does not define this operator or a conversion to a type acceptable to the predefined operator
Solution:
Replace lines like:
(*mat)[i][j] = val; val = (*mat)[i][j];
with:
mat->SetElement(i, j, val); val = mat->GetElement(i, j);
References:
- https://github.com/Slicer/Slicer/commit/375e69d37326d6280fea9100ba786267feae46c4 (COMP: Update usage of legacy vtkMatrix4x4 method)
- https://github.com/Kitware/VTK/commit/d9c5ca0 (Mark legacy methods as "legacy".)
Removed vtksys/ios, vtksys/stl compatibility layers
VTK has removed the vtksys/ios and vtksys/stl compatibility layers.
Error message similar to:
Cannot open include file: 'vtksys/ios/iostream': No such file or directory
Solution:
Replace lines like:
#include <vtksys/ios/iostream> #include <vtksys/ios/sstream> vtksys_ios::ofstream ofs;
with:
#include <iostream> #include <sstream> std::ofstream ofs;
Replace lines like:
#include <vtksys/stl/string> #include <vtksys/stl/vector> vtksys_stl::string str; vtksys_stl::vector<double> vec;
with:
#include <string> #include <vector> std::string str; std::vector<double> vec;
References:
- https://github.com/msmolens/Slicer/commit/55731d6caa7811df346319fdbf6f1d7c6036a31a (COMP: Remove usage of #include <vtksys/stl/*>)
- https://github.com/Kitware/VTK/commit/eaf0f6ac1bc2f65770adf5ff6f17759485cd8072 (ENH: Remove use of include <vtksys/stl/*> and vtksys_stl::*)
- https://github.com/Kitware/VTK/commit/3ae7dd3a106d6b59380e35dfe6962a8c849316c8 (ENH: Remove use of include <vtksys/ios/*> and vtksys_ios::*)
vtkDataArray refactored
VTK has refactored the vtkDataArray class hierarchy.
Error message similar to:
'class XXX’ has no member named ‘SetTupleValue’
Solution:
Replace lines like:
array->GetTupleValue(0, val); array->SetTupleValue(0, val); array->InsertTupleValue(i, val); array->InsertNextTupleValue(val);
with:
array->GetTypedTuple(0, val); array->SetTypedTuple(0, val); array->InsertTypedTuple(i, val); array->InsertNextTypedTuple(val);
Replace lines like:
#include <vtkDataArrayTemplate.h> <any reference to "vtkDataArrayTemplate">
with:
#include <vtkAOSDataArrayTemplate.h> <reference to "vtkAOSDataArrayTemplate">
References:
- https://github.com/msmolens/Slicer/commit/fedb69d0e622d00c81102461ab13909f96341528 (COMP: Update usage of deprecated vtkDataArrayTemplate)
- https://github.com/Kitware/VTK/commit/06e98d0 (Replace vtkDataArrayTemplate with vtkAoSDataArrayTemplate.)
- https://github.com/Kitware/VTK/commit/893fb6e (Refactor data array APIs.)
- http://public.kitware.com/pipermail/vtkusers/2016-May/095388.html ([vtkusers] Wiki examples fail to build due to no InsertNextTypedTuple)
- https://github.com/Kitware/VTK/blob/master/Documentation/Doxygen/ChangesVTK-7-1.md#vtkdataarray-refactor-vtkarraydispatch-and-related-tools
Deprecated pipeline update methods
VTK has deprecated and replaced certain pipeline update methods.
Error message similar to:
'class XXX’ has no member named ‘SetUpdateExtent’
Solution:
Follow suggestions in VTK changelog: https://github.com/Kitware/VTK/blob/master/Documentation/Doxygen/ChangesVTK-7-1.md#pipeline-update-methods
References:
- https://github.com/msmolens/Slicer/commit/f1b93c432dd9ab3f93987c656e3947b90a7cdb45 (COMP: Update usage of deprecated SetUpdateExtent methods)
- https://github.com/Kitware/VTK/commit/f020ebb
Updated Python wrapping
VTK has updated its Python wrapping to support Python 3 and to wrap more code by default.
Symptoms:
Compile or link error while building a Python-wrapped class or library.
Solution 1:
In CMakeLists.txt, add the WRAP_EXCLUDE_PYTHON property anywhere that the WRAP_EXCLUDE property is defined on source files.
Solution 2:
Replace lines of code like:
//BTX ... //ETX
with:
#ifndef __VTK_WRAP__ ... #endif // __VTK_WRAP__
References:
- https://github.com/msmolens/Slicer/commit/10a69c987dcdd4abdbaa74afad22f4af3c16b417 (COMP: Update usage of WRAP_EXCLUDE for Python)
- https://github.com/msmolens/Slicer/commit/d5ca934951cf411c2a4b9b56de533a07e265dcaf (COMP: Update excluding singleton intializers for wrapping)
- https://github.com/Kitware/VTK/commit/e6f75b9 (Wrap many more classes with python.)
- https://github.com/Kitware/VTK/commit/55878a2 (Manual search & destroy of (B|E)TX)
- http://www.vtk.org/Wiki/VTK/Python_Wrapper_Enhancement
- http://www.vtk.org/Wiki/VTK/Python_Wrapping_FAQ
vtkMTimeType
VTK introduced a specific type—vtkMTimeType—to be used for vtkObject modified time (MTime).
Error message similar to:
vtkDiffusionTensorGlyph::GetMTime': overriding virtual function return type differs and is not covariant from 'vtkObject::GetMTime'
Solution:
Update code that uses unsigned long or unsigned long int for modified times to use vtkMTimeType.
References:
vtkStandardNewMacro expects vtkObjectFactory.h
Error message similar to:
expected constructor, destructor, or type conversion before ‘;’ token vtkStandardNewMacro(vtkSlicerModelClipLogic);
Solution:
Add:
#include <vtkObjectFactory.h>
Transition from VTK5 to VTK6
This section documents required change to make Slicer code work with VTK6.
Use SetInputPolyDataConnection and GetPolyDataConnection
Starting with Slicer r24801, support for VTK5 API was removed and VTK6 API should be used.
Error message similar to:
error: ‘class vtkMRMLModelDisplayNode’ has no member named ‘SetInputPolyData’ resultDisplay->SetInputPolyData(resultModel->GetPolyData()); ^
Solution:
Replace occurrences of SetInputPolyData/GetPolyData with SetInputPolyDataConnection/GetPolyDataConnection.
Use AllocateScalars
Starting with Slicer r24801, support for VTK5 API was removed and VTK6 API should be used.
Error message similar to:
error: no matching function for call to ‘vtkImageData::SetNumberOfScalarComponents(int)’ mp_phi->SetNumberOfScalarComponents(1); ^
Solution:
Replace lines like:
imageData->SetScalarTypeToUnsignedShort(); imageData->SetNumberOfScalarComponents(1); // image holds one value intensities imageData->AllocateScalars(); // allocate storage for image data
with:
imageData->AllocateScalars(VTK_UNSIGNED_SHORT, 1); // allocate storage for image data