13. Qt5 to Qt6 (DevEnv5 to DevEnv6) Migration Guide

Qt has two big families of Widgets: Quick/Qml and QtWidgets. The Control UI Guidelines indicates that for ELT GUIs developers should only use widgets that belong QtWidgets family of Widgets. They are stable, and their code receive only fixes. Quick/Qml are modern and are prone to changes. It is becoming a stable library, but its adoption pace of new features is fast.

QtWidgets module has few updates in their port to Qt6 than Quick/Qml, so the migration is relatively easy.

C++ major changes:

  • QList and QVector have a new implementation, and QVector is a typedef to QList. IMPORTANT

  • QProcess::start have changed its behaviour.

  • QtOpenGL module and OpenGL classes have been restructured.

  • QtSvg module have been separated into two: QtSvg and QtSvgWidgets.

  • QtGui: QBitmap: Implicit construction of a QBitmap from a QPixmap is no longer supported

  • QtGui: Affected by the QtOpenGL module restructure. [https://doc.qt.io/qt-6/portingguide.html](https://doc.qt.io/qt-6/portingguide.html)

Python mayor changes:

  • Enums: Python3 recommends the use of fully qualified names to identify the values of enumerations. Avoid the use of self.<enum_value>, and instead use <EnumerationName>.<enum_value>. Taurus and PySide6 has compatibility implemented with the previous notation, but this is a recommendation from python coding standards.

  • exec: no longer a python reserved keyword. (QApplication.exec_() to QApplication.exec() ). Taurus and PySide6 still includes the older notation for compatibility, but you should move to the newer notation.

  • Please move from ‘import PySide2’ to ‘import taurus.external.qt’: this will make your code compatible with both current versions, and future versions of Qt. If your code cannot depend on taurus (ifw-sequencer), then replace for ‘import PySide6’.

13.1. wscript (Project)

TIP: An updated version of the python_application example project is available at: https://gitlab.eso.org/ecos/cut/-/blob/master/examples/python_application, along with the DevEnv5 version in its respective maintenance branch: https://gitlab.eso.org/ecos/cut/-/blob/2.x/examples/python_application/wscript?ref_type=heads (useful for comparison)

To start your migration, you first stop is the project’s wscript.

declare_project() has two mayor changes:

  • requires keyword argument now uses a version-less qt string to add it to waf configuration

  • qt new keyword argument that configures the version of qt to use.

DevEnv5 version

declare_project(
    'qt-widgets',
    version='5.0.0-pre3',
    requires='cxx qt5 boost gtest python',
    recurse='cpp',
    boost=dict(libs='filesystem'),
)

If you want to keep compatibility Qt5 compatibility, please use the following solution.

DevEnv5/6 version

def qtcallable(cnf):
    # We can do some checks here, for example to discriminate between Qt6 and Qt5:
    if os.environ.get("ELT_RELEASE", "6").startswith("6"):
        return dict(version=6)
    else:
        return dict(version=5)


declare_project(
    'qt-widgets',
    version='5.0.0-pre3',
    requires='cxx qt boost gtest python',
    recurse='cpp',
    boost=dict(libs='filesystem'),
    qt=qtcallable
)

13.2. wscript (Configuration)

In case your configuration section of the project configure extra Qt libraries, please replace the old for the new versions:

DevEnv5

cnf.check_python_module('PySide2', condition="ver >= num(5, 14, 1)")
for pkg in 'Qt5Svg Qt5Designer shiboken2'.split():
    cnf.check_cfg(package=pkg, uselib_store=pkg)

DevEnv6

cnf.check_python_module('PySide6')
for pkg in 'Qt6Svg Qt6Designer shiboken6'.split():
    cnf.check_cfg(package=pkg, uselib_store=pkg)

NOTE: you then have to changes the use statements in the module where the library is included/linked.

DevEnv5/6

We use version-agnostic uselib_store variables.

qt_pkgs = ''
if hasattr(cnf, "want_qt6") and cnf.want_qt6 is True:
    qt_pkgs = 'shiboken6 pyside6'
    cnf.check_python_module('PySide6', condition="ver >= num(6, 0, 0)")
    cnf.check_cfg(package="Qt6Svg", uselib_store="QTSVG", args='--cflags --libs')
    cnf.check_cfg(package="Qt6Designer", uselib_store="QTDESIGNER", args='--cflags --libs')
else:
    qt_pkgs = 'shiboken2 pyside2'
    cnf.check_python_module('PySide2', condition="ver >= num(5, 14, 1)")
    cnf.check_cfg(package="Qt5Svg", uselib_store="QTSVG", args='--cflags --libs')
    cnf.check_cfg(package="Qt5Designer", uselib_store="QTDESIGNER", args='--cflags --libs')
for pkg in qt_pkgs.split():
    cnf.check_cfg(package=pkg, uselib_store=pkg, args='--cflags --libs')

NOTE: you then have to changes the use statements in the module where the library is included/linked.

13.3. wscripts (modules)

All auto wtools method to declare Qt applications and modules have dropped the number from the method name. This change has been introduced also in DevEnv5, to allow a period of time to implemented these changes.

These require modifications.

  • declare_qt5cshlib() -> declare_qtcshlib()

  • declare_qt5cprogram() -> declare_qtcprogram()

  • declare_pyqt5package() -> declare_pyqtpackage()

  • declare_pyqt5program() -> declare_pyqtprogram()

In a similar manner, C++ shared libraries that compile into a Qt Designer plugin required a special keywork argument. This also has to drop the number in their name:

DevEnv5 wscript

declare_qtcshlib(target='elt-qt-widgets-plugin',
                 use='cpp.elt-qt-widgets.widgets',
                 qt5_plugin=True)

DevEnv5/6 wscript

declare_qtcshlib(target='elt-qt-widgets-plugin',
                 use='cpp.elt-qt-widgets.widgets',
                 qt_plugin=True)

13.4. Python Code changes

Your .py files require that you change the import statements from using directly PySide2 to the use of taurus external qt module. This module has the same structure that the PySide2 modules, and makes the adoption of future versions of Qt easier.

import PySide2 -> import taurus.external.qt

The declaration of signals has also become stricter. PySide2 allowed to use tuples or lists to declare a signal with multiple argument type signatures. See the following example:

my_signal = Signal([int], [int, str])

PySide6 now requires this to be a tuple. Using a list can lead to runtime exceptions being thrown indicating the signature cannot be found. For the above example, the code should be adjusted as follows:

my_signal = Signal((int,), (int, str))

When a class has multiple inheritance, and one of them is a QObject, the following issue could appear, depending on the __init__ implementation:

QObject.__init__(self, parent=parent, *args, **kwargs)
TypeError: Logger.__init__() got an unexpected keyword argument 'parent'

Code from DevEnv5 that looks like this:

class NewClass(QObject, Logger):

    def __init__(self, parent=None):
        QObject.__init__(self, parent=parent)
        Logger.__init__(self)

In DevEnv6 we recommend to remove the parent keyword while invoking the QObject’s init method.

class NewClass(QObject, Logger):

    def __init__(self, parent=None):
        QObject.__init__(self, parent)
        Logger.__init__(self)

This is a workaround, as the proper solution (using super()) yields the same error.

13.5. Python Bindings for Qt Widgets

The Shiboken “generator” script and wscript has been unified into only one wscript. It drops the legacy-hardcoded entries, and instead uses the pkg-config entries provided now by the official Qt RPMs.

Important

This is a mayor change that may require expert help. Please contact Arturo Hoffstadt Urrutia <ahoffsta@eso.org> if you have issues with a Qt widget bound to python (m1-gui, ddt, cii-srv).

The generator.sh script is gone. It used to be in charge of preparing a long list of arguments for the shiboken executable, generating the code from the typesystem files. Then the bindings/wscript compile the C++ products from shiboken into a C++ shared library with Qt libraries.

Now, bindings/wscript do both steps. A generation step prepares the list of arguments using now pkg-config entries it extracts during the configuration part of the wscript. Then next compilation step is very similar to its previous version, but we have eliminated several unnecessary option or replaced then with pkg-config entries.

The new script is explained with details in Bindings.

13.6. Others

UI files and rcc files are not affected. There are independent of Qt version, and when setting in the project the qt version to use, the proper version of the code generator from Qt will be used.