Documentation/Nightly/Developers/Tutorials/MigrationGuide/VTK7-Qt4-to-VTK8-Qt5

From Slicer Wiki
Jump to: navigation, search
Home < Documentation < Nightly < Developers < Tutorials < MigrationGuide < VTK7-Qt4-to-VTK8-Qt5

Transition from VTK7-Qt4 to VTK9-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:

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

VTK9: 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)