Repository: ifm/nexxT (language: python)

Issue:
implement blocking/nonblocking connections
add a connection attribute "width" for specifying the maximum number of pending samples in inter-thread connections. If this is set to 0, the connection is set to non-blocking (resulting in infinite number of pending samples) and the connection is not taking part in the deadlock cycle check.


Discussion / hints:

Fix patch:
diff --git a/.readthedocs.yml b/.readthedocs.yml
index a7e02a4..001897b 100644
--- a/.readthedocs.yml
+++ b/.readthedocs.yml
@@ -4,20 +4,25 @@
 # Required
 version: 2
 
+# Set the version of Python and other tools you might need
+build:
+  os: ubuntu-20.04
+  tools:
+    python: "3.9"
+    # You can also specify other tool versions:
+    # nodejs: "16"
+    # rust: "1.55"
+    # golang: "1.17"
+
 # Build documentation in the docs/ directory with Sphinx
 sphinx:
   configuration: doc/manual/conf.py
 
-# Build documentation with MkDocs
-#mkdocs:
-#  configuration: mkdocs.yml
-
 # Optionally build your docs in additional formats such as PDF
 formats:
   - pdf
 
-# Optionally set the version of Python and requirements required to build your docs
+# Optionally declare the Python requirements required to build your docs
 python:
-  version: 3.7
   install:
     - requirements: doc/requirements.txt
diff --git a/README.md b/README.md
index 067db89..4cf2df4 100644
--- a/README.md
+++ b/README.md
@@ -8,7 +8,7 @@
 - A state machine guarantees consistent behaviour in initialization, active and shutdown phases across plugins and threads.
 - Non-intrusive design. Unlike other frameworks, nexxT tries its best to leave the developers the freedom they need. No directory structures are predefined, no build tools are required, the data formats are not predefined.
 - Rapid prototyping of algorithms using python, both online (i.e. using an active sensor) and offline (i.e. using data from disk).
-- Visualization can be done using python visualization toolkits supporting QT5.
+- Visualization can be done using python visualization toolkits supporting QT6.
 - Efficient pipelines can be built without interacting with the python interpreter by using only C++ plugins.
 - The open source license gives freedom to adapt code if necessary.
 - Cross platform compatibility for windows and linux.
@@ -41,22 +41,48 @@ Assuming that you have a python3.7+ interpreter in your path, the installation i
     python -m pip install pip -U
     pip install nexxT
 
-## Building from source
+## Porting from nexxT 0.x to nexxT 1.x (aka PySide2 to PySide6)
+
+### Python
+
+The main change for nexxT 1.x is the update from QT5/PySide2 to QT6/PySide6. For flexibility reasons, nexxT now provides a meta package nexxT.Qt, which can be used instead of PySide6. So it is now recommended to replace
+
+    from PySide2 import xyz
+    from PySide2.QtWidgets import uvw
 
-Building from source requires a QT5 installation suited to the PySide2 version used for the build. It is ok to use 5.14.0 to build against all versions 5.14.x of PySide2 because of QT's binary compatibility. You have to set the environment variable QTDIR to the installation directory of QT. Note that this installation is only used during build time, at runtime, nexxT always uses the QT version shipped with PySide2.
+with
 
-On linux, you will be caught by the following known bug from QT: https://bugreports.qt.io/browse/QTBUG-80922?focusedCommentId=558603&page=com.atlassian.jira.plugin.system.issuetabpanels%3Acomment-tabpanel#comment-558603. Apply the fix mentioned in the linked comment.
+    from nexxT.Qt import xyz
+    from nexxT.Qt.QtWidgets import uvw
+    
+In the future, this approach might be also used to support PyQt6, so using nexxT.Qt is recommended over the also possible direct usage of PySide6. Note that the implementation of nexxT.Qt imports the PySide moduels on demand using sys.meta_path, so unused QT modules are not loaded.
+
+Note the porting guide of PySide6: https://doc.qt.io/qtforpython/porting_from2.html: QAction and QShortcut have been moved from QtWidgets to QtGui.
 
-On linux, you will also need llvm and clang installed (because of the shiboken2 dependency). You might need to set the environment variable LLVM_INSTALL_DIR.
+### C++
 
-The following commands build nexxT from source using the non-recommended pip package of shiboken2-generator.
+For c++, nexxT includes shall be prefixed with nexxT/, for example
+
+    #include "Filters.hpp"
+    
+has to be replaced with
+
+    #include "nexxT/Filters.hpp"
+
+## Building from source
+
+Building from source requires a QT6 installation suited to the PySide6 version used for the build. It is ok to use 6.4.0 to build against all versions 6.4.x of PySide6 because of QT's binary compatibility. You have to set the environment variable QTDIR to the installation directory of QT. Note that this installation is only used during build time, at runtime, nexxT always uses the QT version shipped with PySide6.
+
+On linux, you will also need llvm and clang installed (because of the shiboken6 dependency). You might need to set the environment variable LLVM_INSTALL_DIR.
+
+The following commands build nexxT from source using the non-recommended pip package of shiboken6-generator.
 
     git clone https://github.com/ifm/nexxT.git
     cd nexxT/workspace
     python3 -m venv venv
     source venv/bin/activate
     python3 -m pip install pip -U
-    pip install -r requirements.txt --find-links https://download.qt.io/official_releases/QtForPython/shiboken2-generator/
+    pip install -r requirements.txt --find-links https://download.qt.io/official_releases/QtForPython/shiboken6-generator/
     export QTDIR=<path>/<to>/<qt>
     export LLVM_INSTALL_DIR=<path>/<to>/<llvm>
     scons -j 8 ..
@@ -71,6 +97,3 @@ Originally we started with a commercial product from the automotive industry in
 - The data transport layer of *ROS2* seemed not to fulfill our requirements. We have often use cases where we record data to disk and develop algorithms using that data. Because *ROS2* is mainly designed as a system where algorithms run online, its assumptions about the data transport is low latency in prior of reliability. If in question, *ROS2* decides to throw away messages, and this is very bad for the offline/testing usage when you have not such a big focus on algorithm runtime but maybe more on algorithm quality performance. In this use-case it is a reasonable model that slow algorithms shall be able to slow down the computation, but at the time of evaluation this was not easily possible in *ROS2*. A discussion about this topic can be found here: https://answers.ros.org/question/336930/ros2-fast-publisher-slow-subscriber-is-it-possible-to-slow-down-the-publisher/
 - It's not easily possible to start two *ROS2* applications side by side.
 
-## Current Status
-
-The current status is still in an early phase. We use the framework in newer projects, but there is still the chance for API-breaking changes. Some aspects like error handling are still a little rough.
diff --git a/doc/c++/Doxyfile b/doc/c++/Doxyfile
index 7edd104..2d03066 100644
--- a/doc/c++/Doxyfile
+++ b/doc/c++/Doxyfile
@@ -790,13 +790,15 @@ WARN_LOGFILE           =
 # spaces. See also FILE_PATTERNS and EXTENSION_MAPPING
 # Note: If this tag is empty the current directory is searched.
 
-INPUT                  = ../../nexxT/src/DataSamples.hpp \
-                         ../../nexxT/src/Filters.hpp \
-                         ../../nexxT/src/Ports.hpp \
-                         ../../nexxT/src/PropertyCollection.hpp \
-                         ../../nexxT/src/Services.hpp \
-                         ../../nexxT/src/Logger.hpp \
-                         ../../nexxT/src/NexxTPlugins.hpp
+INPUT                  = ../../nexxT/src
+#                         ../../nexxT/src/DataSamples.hpp \
+#                         ../../nexxT/src/Filters.hpp \
+#                         ../../nexxT/src/Ports.hpp \
+#                         ../../nexxT/src/PropertyCollection.hpp \
+#                         ../../nexxT/src/Services.hpp \
+#                         ../../nexxT/src/Logger.hpp \
+#                         ../../nexxT/src/NexxTPlugins.hpp \
+#                         ../../nexxT/src/SharedPointerTypes.hpp
 
 # This tag can be used to specify the character encoding of the source files
 # that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses
diff --git a/doc/manual/conf.py b/doc/manual/conf.py
index 0875ccf..99b5f4a 100644
--- a/doc/manual/conf.py
+++ b/doc/manual/conf.py
@@ -65,7 +65,7 @@
 #
 # This is also used if you do content translation via gettext catalogs.
 # Usually you set "language" from the command line for these cases.
-language = None
+language = 'en'
 
 # List of patterns, relative to source directory, that match files and
 # directories to ignore when looking for source files.
@@ -230,6 +230,12 @@ def skip(app, what, name, obj, would_skip, options):
                 "DESTRUCTED"]:
         # skip the state machine entries
         return True
+    if name == "Services":
+        if not hasattr(skip, "srvseen"):
+            skip.srvseen = set()
+        if obj in skip.srvseen:
+            return True
+        skip.srvseen.add(obj)
     return would_skip
 
 def remove_apidoc_artifacts(app):
diff --git a/doc/manual/example-deadlock-fixed.png b/doc/manual/example-deadlock-fixed.png
new file mode 100644
index 0000000..00c62bf
Binary files /dev/null and b/doc/manual/example-deadlock-fixed.png differ
diff --git a/doc/manual/example-deadlock.png b/doc/manual/example-deadlock.png
new file mode 100644
index 0000000..fb95396
Binary files /dev/null and b/doc/manual/example-deadlock.png differ
diff --git a/doc/manual/nexxT.rst b/doc/manual/nexxT.rst
index f59aacd..752d313 100644
--- a/doc/manual/nexxT.rst
+++ b/doc/manual/nexxT.rst
@@ -11,6 +11,9 @@ Subpackages
    autogenerated/nexxT.services
    autogenerated/nexxT.filters
    autogenerated/nexxT.examples
+   autogenerated/nexxT.Qt
+   autogenerated/nexxT.QtMetaPackage
+   autogenerated/nexxT.shiboken
 
 Module contents
 ---------------
diff --git a/doc/manual/tutorial.rst b/doc/manual/tutorial.rst
index 8975327..82e088a 100644
--- a/doc/manual/tutorial.rst
+++ b/doc/manual/tutorial.rst
@@ -149,6 +149,31 @@ Now it's time to save the configuration in the tool bar and test it. Initialize
 
 .. image:: example-first-app-running.png
 
+Thread cycles and Deadlocks
++++++++++++++++++++++++++++
+
+When you get error messages like
+
+.. code-block:: console
+
+    nexxT.core.ActiveApplication: This graph is not deadlock-safe. A cycle has been found in the thread graph: main->compute->main
+
+you have tried to create an application which is potentially deadlocking. In the message above, it is stated that
+there is a dependency cycle in the filters of the threads main and compute. This is the corresponding filter graph:
+
+.. image:: example-deadlock.png
+
+The reason for the possible deadlocks is that nexxT by default uses a semaphore to control the number of pending samples in inter-thread connections. When transmitting a sample to another thread which has not yet processed the last transmitted sample, the transmitting thread blocks until the last transmitted sample has been received by the receiving thread. This behaviour might cause deadlocks in the presence of cycles, nexxT detects these cycles and refuses to execute these applications.
+
+There are multiple solutions for this issue:
+
+- move filters to other threads. The above examples gets deadlock safe when moving the filter *filt_gui* from the gui thread (green) to the compute thread (dark-blue).
+- Moving all filters to the main thread is always a solution, but this might be too slow.
+- Use non-blocking connections for specific inter-thread connections (right-click on a connection and select *Set non blocking*). Non-blocking connections do not check for pending data samples, so they cannot cause deadlocks. Non blocking connections are displayed in red in the filter graph:
+
+.. image:: example-deadlock-fixed.png
+
+Note that non-blocking connections come with the risk that a potentially infinite amount of data is pending on inter-thread connections. This might cause high latency or even out-of-memory situations. Therefore, non-blocking connections are not recommended to be used at high data rate connections. Output ports triggered by sporadic events are best suited for non-blocking connections.
 
 Developer Perspectives
 ----------------------
@@ -256,13 +281,13 @@ The onClose(...) method is the inverse of onOpen(...). It releases the widget fr
 C++
 +++
 
-**What are the benefits of using C++ instead of python?** While you can argue that performance is not affected much if the filter only uses a wrapper around a library such as opencv, the `python GIL <https://wiki.python.org/moin/GlobalInterpreterLock>`_ is a factor which might limit performance in a multithreaded application like nexxT. In a nutshell it means, that whenever python code is executed, the interpreter has the GIL locked to prevent other threads from modifying interpreter states. C extensions like numpy, opencv or PySide2 unlock the GIL during long-duration calls. As a consequence, heavily using pure python will slow down other threads because the GIL limits parallel execution. Using C++ filters, it is possible to design operations which are not affected by the GIL at all.
+**What are the benefits of using C++ instead of python?** While you can argue that performance is not affected much if the filter only uses a wrapper around a library such as opencv, the `python GIL <https://wiki.python.org/moin/GlobalInterpreterLock>`_ is a factor which might limit performance in a multithreaded application like nexxT. In a nutshell it means, that whenever python code is executed, the interpreter has the GIL locked to prevent other threads from modifying interpreter states. C extensions like numpy, opencv or PySide6 unlock the GIL during long-duration calls. As a consequence, heavily using pure python will slow down other threads because the GIL limits parallel execution. Using C++ filters, it is possible to design operations which are not affected by the GIL at all.
 
 Filters in C++ are very similar to filters in python. They are defined using a class inheriting from :cpp:class:`nexxT::Filter` and overwriting the same methods just like in python. One difference is the usage of nexxT services like the MainWindow service (see :ref:`tutorial:Display filters`). In C++, these services are of type QObject. Therefore, you need to use `QMetaObject::invokeMethod <https://doc.qt.io/qt-5/qmetaobject.html#invokeMethod>`_ for accessing slots of the services.
 
-The plugin library links against the nexxT runtime library (*libnexxT.so* or *nexxT.dll*) which is provided in nexxT installation directory. It also links against a QT library used for development. Note that during runtime, the QT library bundled with PySide2 will be used regardless of which QT library has been used to develop. To be on the safe side, you should use a matching major.minor version, the patch level should be non-relevant. For example, to compile a plugin for the PySide2 version 5.14.2.3, you can use QT 5.14.0. Plugin libraries do not use shiboken2 for exposing the filters in python, instead they use a QLibrary interface.
+The plugin library links against the nexxT runtime library (*libnexxT.so* or *nexxT.dll*) which is provided in nexxT installation directory. It also links against a QT library used for development. Note that during runtime, the QT library bundled with PySide6 will be used regardless of which QT library has been used to develop. To be on the safe side, you should use a matching major.minor version, the patch level should be non-relevant. For example, to compile a plugin for the PySide6 version 5.14.2.3, you can use QT 5.14.0. Plugin libraries do not use shiboken2 for exposing the filters in python, instead they use a QLibrary interface.
 
-Note that - unlike pure QT - PySide2 does not provide any compatibility guarantees between minor or patch level releases. This means that it is generally not possible to use nexxT with a different PySide2 version than it was compiled against. 
+Note that - unlike pure QT - PySide6 does not provide any compatibility guarantees between minor or patch level releases. This means that it is generally not possible to use nexxT with a different PySide6 version than it was compiled against. 
 
 Each plugin library can announce one or more filter classes.
 
diff --git a/doc/requirements.txt b/doc/requirements.txt
index 9b814ad..a423237 100644
--- a/doc/requirements.txt
+++ b/doc/requirements.txt
@@ -1,8 +1,10 @@
 -e .
+-e .
 Sphinx==3.3.1
-sphinx-rtd-theme==0.5.0
+sphinx-rtd-theme==1.1.1
 sphinxcontrib-apidoc==0.3.0
 breathe==4.25.1
 commonmark==0.9.1
 docutils==0.16
 recommonmark==0.7.1
+Jinja2<3.1.0
diff --git a/nexxT/Qt.py b/nexxT/Qt.py
new file mode 100644
index 0000000..bbc93d5
--- /dev/null
+++ b/nexxT/Qt.py
@@ -0,0 +1,9 @@
+"""
+This module is never imported when executing nexxT. Instead, the Qt module is provided through the QtMetaPackage
+which is preventing python from loading unnecessary extensions. However, it is very handy to have this module, so
+pylint and the IDE auto-completers are happy to know the contents of this module
+"""
+import logging
+from PySide6 import QtWidgets, QtCore, QtGui
+
+logging.getLogger(__name__).warning("nexxT.Qt imported not via QtMetaPackage!")
diff --git a/nexxT/QtMetaPackage.py b/nexxT/QtMetaPackage.py
new file mode 100644
index 0000000..9b4183c
--- /dev/null
+++ b/nexxT/QtMetaPackage.py
@@ -0,0 +1,110 @@
+"""
+This module provides a QT meta package such that we are able to write "from nexxT.Qt.QtWidgets import QWidget" and
+nexxT.Qt will serve as an alias for PySide6
+
+It is loosly based on this tutorial:
+https://dev.to/dangerontheranger/dependency-injection-with-import-hooks-in-python-3-5hap
+"""
+import importlib.abc
+import importlib.machinery
+import importlib.util
+import sys
+import types
+
+class QtFinder(importlib.abc.MetaPathFinder):
+    """
+    The meta path finder which will be added to sys.meta_path
+    """
+    def __init__(self, loader):
+        self._loader = loader
+
+    def find_spec(self, fullname, path, target=None):
+        """
+        Attempt to locate the requested module
+
+        :param fullname: the fully-qualified name of the module,
+        :param path: set to __path__ for sub-modules/packages, or None otherwise.
+        :param target: can be a module object, but is unused here.
+        """
+        if self._loader.provides(fullname):
+            return importlib.machinery.ModuleSpec(fullname, self._loader)
+
+class QtLoader(importlib.abc.Loader):
+    """
+    The actual loader which maps PySide modules to nexxT.Qt. The approach is similar to executing a
+    ``from PySide6 import *`` statement in a real Qt.py module, but it is dynamically and prevents from loading unused
+    QT modules, like QtMultimedia, etc.
+    """
+
+    def __init__(self, prefix, qtlib):
+        """
+        Constructor.
+
+        :param prefix: the prefix for the proxied library (e.g. nexxT.Qt)
+        :param qtlib: the PySide library to be used, must be PySide6
+        """
+        self._prefix = prefix
+        self._qtlib = qtlib
+
+    def provides(self, fullname):
+        """
+        Checks whether the queried module can be provided by this laoder or not.
+
+        :param fullname: full-qualified module name.
+        """
+        return fullname == self._prefix or fullname.startswith(self._prefix + ".")
+
+    def create_module(self, spec):
+        """
+        Creates a new module according to spec. It will be empty initially and populated during exec_module(...).
+
+        :param spec: A ModuleSpec instance.
+        """
+        res = types.ModuleType(spec.name)
+        return res
+
+    def exec_module(self, module):
+        """
+        This function is called after create_module and it populates the module's namespace with the corresponding
+        instances from PySideX.
+        """
+        proxyname = self._qtlib + module.__name__[len(self._prefix):]
+        proxymod = importlib.import_module(proxyname)
+        def _copy_attrs(src, dst):
+            """
+            pyqtgraph approach for creating mirrors of the Qt modules
+            """
+            for o in dir(src):
+                if isinstance(getattr(src,o), types.ModuleType):
+                    continue
+                if o == "__path__":
+                    setattr(dst, o, [])
+                    continue
+                if o.startswith("__") and o != "__version__" and o != "__version_info__":
+                    continue
+                if not hasattr(dst, o):
+                    setattr(dst, o, getattr(src, o))
+        _copy_attrs(proxymod, module)
+        if proxyname == "PySide6":
+            module.call_exec = lambda instance, *args, **kw: instance.exec(*args, **kw)
+
+    def _truncate_name(self, fullname):
+        """Strip off _COMMON_PREFIX from the given module name
+        Convenience method when checking if a service is provided.
+        """
+        return fullname[len(self._prefix):]
+
+def setup():
+    libs = []
+    try:
+        import PySide6
+        libs.append(("PySide6", "shiboken6"))
+    except ImportError:
+        pass
+    if len(libs) != 1:
+        raise RuntimeError("nexxT needs PySide6 installed (available: %s).", libs)
+    libs = libs[0]
+    sys.meta_path = ([QtFinder(QtLoader("nexxT.Qt", libs[0])), QtFinder(QtLoader("nexxT.shiboken", libs[1]))] +
+                     sys.meta_path)
+
+setup()
diff --git a/nexxT/__init__.py b/nexxT/__init__.py
index a5c337e..f1e5e4a 100644
--- a/nexxT/__init__.py
+++ b/nexxT/__init__.py
@@ -8,7 +8,18 @@
 Setup the logging here until we have a better place.
 """
 
+cnexxT = None # pylint: disable=invalid-name
+useCImpl = True # pylint: disable=invalid-name
+__version__ = None # pylint: disable=invalid-name
+
 def setup():
+    """
+    Sets up the nexxT environment. In particular, provides the cnexxT and the Qt packages under the nexxT nameespace.
+    """
+    # create an alias nexxT.Qt for PySide[26]
+    # pylint: disable=import-outside-toplevel
+    import nexxT.QtMetaPackage
+
     try:
         from importlib import metadata
     except ImportError:
@@ -21,7 +32,8 @@ def setup():
     import platform
 
     logger = logging.getLogger()
-    INTERNAL = 5 # setup log level for internal messages
+    # setup log level for internal messages
+    INTERNAL = 5 # pylint: disable=invalid-name
     logging.addLevelName(INTERNAL, "INTERNAL")
     logging.INTERNAL = INTERNAL
     def internal(self, message, *args, **kws):
@@ -36,15 +48,15 @@ def internal(self, message, *args, **kws):
     logger.info("configured logger")
     logger.setLevel(logging.INFO)
 
-    global __version__
+    global __version__  # pylint: disable=invalid-name
     __version__ = metadata.version("nexxT")
 
-    global useCImpl
+    global useCImpl # pylint: disable=invalid-name
     useCImpl = not bool(int(os.environ.get("NEXXT_DISABLE_CIMPL", "0")))
     if useCImpl:
-        # make sure to import PySide2 before loading the cnexxT extension module because
+        # make sure to import nexxT.Qt before loading the cnexxT extension module because
         # there is a link-time dependency which would be impossible to resolve otherwise
-        import PySide2.QtCore
+        import nexxT.Qt.QtCore
         p = os.environ.get("NEXXT_CEXT_PATH", None)
         if p is None:
             variant = os.environ.get("NEXXT_VARIANT", "release")
@@ -59,8 +71,12 @@ def internal(self, message, *args, **kws):
             p = str(Path(p).absolute())
             logger.info("c extension module search path: %s", p)
             sys.path.append(p)
+        # TODO: following two lines can be removed after this bug has been
+        #       fixed: https://bugreports.qt.io/browse/PYSIDE-1627
+        import nexxT.Qt.QtWidgets
+        import nexxT.Qt.QtGui
         import cnexxT as imp_cnexxT
-        global cnexxT
+        global cnexxT # pylint: disable=invalid-name
         cnexxT = imp_cnexxT
         def setLevel(level):
             ret = setLevel.origFunc(level)
@@ -70,4 +86,4 @@ def setLevel(level):
         logger.setLevel = setLevel
         logger.setLevel(logging.INFO)
 
-setup()
\ No newline at end of file
+setup()
diff --git a/nexxT/core/ActiveApplication.py b/nexxT/core/ActiveApplication.py
index 5fca254..c5478c1 100644
--- a/nexxT/core/ActiveApplication.py
+++ b/nexxT/core/ActiveApplication.py
@@ -9,7 +9,7 @@
 """
 
 import logging
-from PySide2.QtCore import QObject, Slot, Signal, Qt, QCoreApplication
+from nexxT.Qt.QtCore import QObject, Slot, Signal, Qt, QCoreApplication
 from nexxT.interface import FilterState, OutputPortInterface, InputPortInterface
 from nexxT.core.Exceptions import FilterStateMachineError, NexTInternalError, PossibleDeadlock
 from nexxT.core.CompositeFilter import CompositeFilter
@@ -43,8 +43,7 @@ def __init__(self, graph):
         self._interThreadConns = []
         self._operationInProgress = False
         # connect signals and slots
-        for tname in self._threads:
-            t = self._threads[tname]
+        for _, t in self._threads.items():
             t.operationFinished.connect(self._operationFinished)
             # we use a queued connection because we want to be able to connect signals
             # to and from this object after constructor has passed
@@ -86,7 +85,7 @@ def _traverseAndSetup(self, graph, namePrefix=""):
 
     def __del__(self):
         logger.debug("destructor of ActiveApplication")
-        if self._state != FilterState.DESTRUCTING and self._state != FilterState.DESTRUCTED:
+        if self._state not in (FilterState.DESTRUCTING, FilterState.DESTRUCTED):
             logger.warning("ActiveApplication: shutdown in destructor")
             self.cleanup()
         logger.debug("destructor of ActiveApplication done")
@@ -136,7 +135,7 @@ def shutdown(self):
         if self._state == FilterState.CONSTRUCTED:
             self.destruct()
         if not self._state == FilterState.DESTRUCTED:
-            raise NexTInternalError("Unexpected state '%s' after shutdown." % FilterState.state2str(self._state))
+            raise NexTInternalError(f"Unexpected state '{FilterState.state2str(self._state)}' after shutdown.")
 
     def stopThreads(self):
         """
@@ -145,8 +144,8 @@ def stopThreads(self):
         """
         logger.internal("stopping threads...")
         assertMainThread()
-        for tname in self._threads:
-            self._threads[tname].cleanup()
+        for _, t in self._threads.items():
+            t.cleanup()
         self._threads.clear()
 
     @staticmethod
@@ -162,11 +161,13 @@ def _compress(proxy):
                 for idx in range(len(proxy[compName, fromPort])):
                     if proxy[compName, fromPort][idx] is None:
                         continue
-                    proxyNode, proxyPort = proxy[compName, fromPort][idx]
+                    proxyNode, proxyPort, w = proxy[compName, fromPort][idx]
                     # if fromNode is itself a composite node, resolve it
                     if (proxyNode, proxyPort) in proxy:
                         changed = True
                         proxy[compName, fromPort][idx] = None
+                        for _, _, widths in proxy[proxyNode, proxyPort]:
+                            widths.extend(w)
                         proxy[compName, fromPort].extend(proxy[proxyNode, proxyPort])
         # remove None's
         for compName, fromPort in proxy:
@@ -185,19 +186,20 @@ def _calculateProxyPorts(self):
         """
         proxyInputPorts = {}
         proxyOutputPorts = {}
-        for compName in self._composite2graphs:
-            subgraph = self._composite2graphs[compName]
+        for compName, subgraph in self._composite2graphs.items():
             cin_node = "CompositeInput"
             for fromPort in subgraph.allOutputPorts(cin_node):
                 proxyInputPorts[compName, fromPort] = []
                 for _, _, toNode, toPort in subgraph.allConnectionsFromOutputPort(cin_node, fromPort):
-                    proxyInputPorts[compName, fromPort].append((compName + "/" + toNode, toPort))
+                    width = subgraph.getConnectionProperties(cin_node, fromPort, toNode, toPort)["width"]
+                    proxyInputPorts[compName, fromPort].append((compName + "/" + toNode, toPort, [width]))
                 proxyOutputPorts[compName + "/" + cin_node, fromPort] = []
             cout_node = "CompositeOutput"
             for toPort in subgraph.allInputPorts(cout_node):
                 proxyOutputPorts[compName, toPort] = []
                 for fromNode, fromPort, _, _ in subgraph.allConnectionsToInputPort(cout_node, toPort):
-                    proxyOutputPorts[compName, toPort].append((compName + "/" + fromNode, fromPort))
+                    width = subgraph.getConnectionProperties(fromNode, fromPort, cout_node, toPort)["width"]
+                    proxyOutputPorts[compName, toPort].append((compName + "/" + fromNode, fromPort, [width]))
                 proxyInputPorts[compName + "/" + cout_node, toPort] = []
 
         return self._compress(proxyInputPorts), self._compress(proxyOutputPorts)
@@ -208,27 +210,29 @@ def _allConnections(self):
         return all connections of this application including the connections from and to composite nodes
         """
         proxyInputPorts, proxyOutputPorts = self._calculateProxyPorts()
-        allGraphs = set([(n, self._composite2graphs[n]) for n in self._composite2graphs] + [("", self._graph)])
+        allGraphs = set(list(self._composite2graphs.items()) + [("", self._graph)])
         res = []
 
         for namePrefix, graph in allGraphs:
             for fromNode, fromPort, toNode, toPort in graph.allConnections():
+                width = graph.getConnectionProperties(fromNode, fromPort, toNode, toPort)["width"]
                 fromName = namePrefix + "/" + fromNode
                 toName = namePrefix + "/" + toNode
 
                 if (fromName, fromPort) in proxyOutputPorts:
                     src = proxyOutputPorts[fromName, fromPort]
                 else:
-                    src = [(fromName, fromPort)]
+                    src = [(fromName, fromPort, [width])]
 
                 if (toName, toPort) in proxyInputPorts:
                     dest = proxyInputPorts[toName, toPort]
                 else:
-                    dest = [(toName, toPort)]
+                    dest = [(toName, toPort, [width])]
 
                 for s in src:
                     for d in dest:
-                        res.append(s + d)
+                        widths = set(s[2] + d[2] + [width])
+                        res.append((s[:2] + d[:2], 0 if 0 in widths else max(widths)))
         return res
 
     def _setupConnections(self):
@@ -241,7 +245,7 @@ def _setupConnections(self):
         graph = {}
         if self._graphConnected:
             return
-        for fromNode, fromPort, toNode, toPort in self._allConnections():
+        for (fromNode, fromPort, toNode, toPort), width in self._allConnections():
             fromThread = self._filters2threads[fromNode]
             toThread = self._filters2threads[toNode]
             t0 = self._threads[fromThread]
@@ -251,13 +255,14 @@ def _setupConnections(self):
             if toThread == fromThread:
                 OutputPortInterface.setupDirectConnection(p0, p1)
             else:
-                itc = OutputPortInterface.setupInterThreadConnection(p0, p1, self._threads[fromThread].qthread())
+                itc = OutputPortInterface.setupInterThreadConnection(p0, p1, self._threads[fromThread].qthread(), width)
                 self._interThreadConns.append(itc)
                 if not fromThread in graph:
                     graph[fromThread] = set()
                 if not toThread in graph:
                     graph[toThread] = set()
-                graph[fromThread].add(toThread)
+                if width > 0:
+                    graph[fromThread].add(toThread)
 
         def _checkCycle(thread, cycleInfo):
             if thread in cycleInfo:
diff --git a/nexxT/core/AppConsole.py b/nexxT/core/AppConsole.py
index 5dc9ed6..fadfdea 100644
--- a/nexxT/core/AppConsole.py
+++ b/nexxT/core/AppConsole.py
@@ -12,9 +12,11 @@
 import logging
 import signal
 import sys
-from PySide2.QtCore import QCoreApplication, QLocale
-from PySide2.QtGui import QIcon
-from PySide2.QtWidgets import QApplication
+import nexxT
+import nexxT.Qt
+from nexxT.Qt.QtCore import QCoreApplication, QLocale
+from nexxT.Qt.QtGui import QIcon
+from nexxT.Qt.QtWidgets import QApplication
 
 from nexxT.core.Utils import SQLiteHandler, MethodInvoker, waitForSignal
 from nexxT.core.ConfigFiles import ConfigFileLoader
@@ -124,17 +126,17 @@ def cleanup():
 
     for s in execScripts:
         logger.info("Executing script '%s'", s)
-        with open(s) as fscript:
+        with open(s, encoding="utf-8") as fscript:
             # note that exec is used intentionally here to provide the user with scripting possibilities
             exec(compile(fscript.read(), s, 'exec'), code_globals)  # pylint: disable=exec-used
         logger.debug("Executing script done")
 
-    res = app.exec_()
+    res = nexxT.Qt.call_exec(app)
     logger.debug("closing config")
     config.close()
     cleanup()
 
-    logger.internal("app.exec_ returned")
+    logger.internal("app.exec returned")
 
     return res
 
diff --git a/nexxT/core/Application.py b/nexxT/core/Application.py
index c189314..0300a09 100644
--- a/nexxT/core/Application.py
+++ b/nexxT/core/Application.py
@@ -10,7 +10,7 @@
 
 import logging
 import re
-from PySide2.QtCore import QCoreApplication, Qt
+from nexxT.Qt.QtCore import QCoreApplication, Qt
 from nexxT.core.PropertyCollectionImpl import PropertyCollectionImpl
 from nexxT.core.SubConfiguration import SubConfiguration
 from nexxT.core.ActiveApplication import ActiveApplication
diff --git a/nexxT/core/BaseFilterEnvironment.py b/nexxT/core/BaseFilterEnvironment.py
index b599132..ee6d5cc 100644
--- a/nexxT/core/BaseFilterEnvironment.py
+++ b/nexxT/core/BaseFilterEnvironment.py
@@ -9,7 +9,7 @@
 """
 
 import logging
-from PySide2.QtCore import QObject, QThread
+from nexxT.Qt.QtCore import QObject, QThread
 from nexxT import useCImpl
 from nexxT.interface import FilterState
 from nexxT.core.Exceptions import NexTInternalError, UnexpectedFilterState
@@ -17,7 +17,7 @@
 logger = logging.getLogger(__name__)
 
 if useCImpl:
-    import cnexxT
+    import cnexxT # pylint: disable=import-error
     # pylint: disable=invalid-name
     # this is not really a constant, but a class name
     BaseFilterEnvironment = cnexxT.nexxT.BaseFilterEnvironment
diff --git a/nexxT/core/BaseGraph.py b/nexxT/core/BaseGraph.py
index fa1851b..abf48ec 100644
--- a/nexxT/core/BaseGraph.py
+++ b/nexxT/core/BaseGraph.py
@@ -9,16 +9,20 @@
 """
 
 from collections import OrderedDict
-from PySide2.QtCore import QObject, Signal, Slot
+import logging
+from nexxT.Qt.QtCore import QObject, Signal, Slot
 from nexxT.core.Utils import assertMainThread
 from nexxT.core.Exceptions import (NodeExistsError, NodeNotFoundError, PortExistsError, PortNotFoundError,
                                    ConnectionExistsError, ConnectionNotFound, NodeProtectedError)
 
+logger = logging.getLogger(__name__)
+
 class BaseGraph(QObject):
     """
     This class defines a graph where the nodes can have input and output ports and
-    these ports can be connected together. All operations are performed with QT
-    signals and slots.
+    these ports can be connected together. Connections have a property dictionary.
+
+    All operations are performed with QT signals and slots.
     """
     nodeAdded = Signal(str)
     nodeRenamed = Signal(str, str)
@@ -33,12 +37,14 @@ class BaseGraph(QObject):
     connectionDeleted = Signal(str, str, str, str)
     dirtyChanged = Signal()
 
-    def __init__(self):
+    def __init__(self, defaultConnProp=None):
         assertMainThread()
         super().__init__()
         self._protected = set()
         self._nodes = OrderedDict()
         self._connections = []
+        self._connectionProps = {}
+        self._defaultConnProp = {} if defaultConnProp is None else defaultConnProp.copy()
 
     def uniqueNodeName(self, nodeName):
         """
@@ -50,9 +56,9 @@ def uniqueNodeName(self, nodeName):
         if not nodeName in self._nodes:
             return nodeName
         t = 2
-        while "%s%d" % (nodeName, t) in self._nodes:
+        while f"{nodeName}{t}" in self._nodes:
             t += 1
-        return "%s%d" % (nodeName, t)
+        return f"{nodeName}{t}"
 
     def protect(self, name):
         """
@@ -97,7 +103,7 @@ def renameNode(self, oldName, newName):
         of = self._nodes[oldName]
         del self._nodes[oldName]
         self._nodes[newName] = of
-        for i in range(len(self._connections)):
+        for i, c in enumerate(self._connections):
             c = self._connections[i]
             if c[0] == oldName:
                 c = (newName, c[1], c[2], c[3])
@@ -148,8 +154,10 @@ def addConnection(self, nodeNameFrom, portNameFrom, nodeNameTo, portNameTo):
             raise PortNotFoundError(nodeNameTo, portNameTo, "Input")
         if (nodeNameFrom, portNameFrom, nodeNameTo, portNameTo) in self._connections:
             raise ConnectionExistsError(nodeNameFrom, portNameFrom, nodeNameTo, portNameTo)
-        self._connections.append((nodeNameFrom, portNameFrom, nodeNameTo, portNameTo))
-        self.connectionAdded.emit(nodeNameFrom, portNameFrom, nodeNameTo, portNameTo)
+        conn = (nodeNameFrom, portNameFrom, nodeNameTo, portNameTo)
+        self._connections.append(conn)
+        self._connectionProps[conn] = self._defaultConnProp.copy()
+        self.connectionAdded.emit(*conn)
         self.dirtyChanged.emit()
 
     @Slot(str, str, str, str)
@@ -165,8 +173,10 @@ def deleteConnection(self, nodeNameFrom, portNameFrom, nodeNameTo, portNameTo):
         assertMainThread()
         if (nodeNameFrom, portNameFrom, nodeNameTo, portNameTo) not in self._connections:
             raise ConnectionNotFound(nodeNameFrom, portNameFrom, nodeNameTo, portNameTo)
-        self._connections.remove((nodeNameFrom, portNameFrom, nodeNameTo, portNameTo))
-        self.connectionDeleted.emit(nodeNameFrom, portNameFrom, nodeNameTo, portNameTo)
+        conn = (nodeNameFrom, portNameFrom, nodeNameTo, portNameTo)
+        self._connections.remove(conn)
+        del self._connectionProps[conn]
+        self.connectionDeleted.emit(*conn)
         self.dirtyChanged.emit()
 
     @Slot(str, str)
@@ -199,8 +209,7 @@ def deleteInputPort(self, node, portName):
         if not portName in self._nodes[node]["inports"]:
             raise PortNotFoundError(node, portName)
         toDel = []
-        for i in range(len(self._connections)):
-            fromNode, fromPort, toNode, toPort = self._connections[i]
+        for fromNode, fromPort, toNode, toPort in self._connections:
             if (toNode == node and toPort == portName):
                 toDel.append((fromNode, fromPort, toNode, toPort))
         for c in toDel:
@@ -229,11 +238,15 @@ def renameInputPort(self, node, oldPortName, newPortName):
             raise PortExistsError(node, newPortName)
         idx = self._nodes[node]["inports"].index(oldPortName)
         self._nodes[node]["inports"][idx] = newPortName
-        for i in range(len(self._connections)):
-            fromNode, fromPort, toNode, toPort = self._connections[i]
+        for i, oldConn in enumerate(self._connections):
+            (fromNode, fromPort, toNode, toPort) = oldConn
             if (toNode == node and toPort == oldPortName):
                 toPort = newPortName
-            self._connections[i] = (fromNode, fromPort, toNode, toPort)
+            newConn = (fromNode, fromPort, toNode, toPort)
+            self._connections[i] = newConn
+            p = self._connectionProps[oldConn]
+            del self._connectionProps[oldConn]
+            self._connectionProps[newConn] = p
         self.inPortRenamed.emit(node, oldPortName, newPortName)
 
     @Slot(str, str)
@@ -266,8 +279,7 @@ def deleteOutputPort(self, node, portName):
         if not portName in self._nodes[node]["outports"]:
             raise PortNotFoundError(node, portName)
         toDel = []
-        for i in range(len(self._connections)):
-            fromNode, fromPort, toNode, toPort = self._connections[i]
+        for fromNode, fromPort, toNode, toPort in self._connections:
             if (fromNode == node and fromPort == portName):
                 toDel.append((fromNode, fromPort, toNode, toPort))
         for c in toDel:
@@ -296,11 +308,15 @@ def renameOutputPort(self, node, oldPortName, newPortName):
             raise PortExistsError(node, newPortName)
         idx = self._nodes[node]["outports"].index(oldPortName)
         self._nodes[node]["outports"][idx] = newPortName
-        for i in range(len(self._connections)):
-            fromNode, fromPort, toNode, toPort = self._connections[i]
+        for i, oldConn in enumerate(self._connections):
+            (fromNode, fromPort, toNode, toPort) = oldConn
             if (fromNode == node and fromPort == oldPortName):
                 fromPort = newPortName
-            self._connections[i] = (fromNode, fromPort, toNode, toPort)
+            newConn = (fromNode, fromPort, toNode, toPort)
+            self._connections[i] = newConn
+            p = self._connectionProps[oldConn]
+            del self._connectionProps[oldConn]
+            self._connectionProps[newConn] = p
         self.outPortRenamed.emit(node, oldPortName, newPortName)
 
     def allNodes(self):
@@ -331,12 +347,44 @@ def allConnectionsToInputPort(self, toNode, toPort):
     def allConnectionsFromOutputPort(self, fromNode, fromPort):
         """
         Return all connections from the specified port.
+
         :param fromNode: name of node
         :param fromPort: name of port
         :return: a list of 4 tuples of strings (nodeFrom, portFrom, nodeTo, portTo)
         """
         return [(a, b, c, d) for a, b, c, d, in self._connections if (a == fromNode and b == fromPort)]
 
+    def getConnectionProperties(self, nodeFrom, portFrom, nodeTo, portTo):
+        """
+        Return the connection properties of the specified connection
+
+        :param nodeFrom: name of the output node
+        :param portFrom: name of the output port
+        :param nodeTo: name of the input node
+        :param portTo: name of the input port
+        :return: a copy of the property dict (use setConnectionProperties to modify this)
+        """
+        if (nodeFrom, portFrom, nodeTo, portTo) not in self._connectionProps:
+            logger.info("connprops.keys()=%s", self._connectionProps.keys())
+            raise ConnectionNotFound(nodeFrom, portFrom, nodeTo, portTo)
+        return self._connectionProps[nodeFrom, portFrom, nodeTo, portTo].copy()
+
+    def setConnectionProperties(self, nodeFrom, portFrom, nodeTo, portTo, properties):
+        """
+        Set the connection properties of the specified connection to the given dict.
+
+        :param nodeFrom: name of the output node
+        :param portFrom: name of the output port
+        :param nodeTo: name of the input node
+        :param portTo: name of the input port
+        :param properties: a dict with the new properties
+        """
+        if (nodeFrom, portFrom, nodeTo, portTo) not in self._connectionProps:
+            raise ConnectionNotFound(nodeFrom, portFrom, nodeTo, portTo)
+        logger.info("set connprop: %s.%s -> %s.%s (%s)", nodeFrom, portFrom, nodeTo, portTo, properties)
+        self._connectionProps[nodeFrom, portFrom, nodeTo, portTo] = properties
+        self.dirtyChanged.emit()
+
     def allInputPorts(self, node):
         """
         Return all input port names of the node.
diff --git a/nexxT/core/ConfigFileSchema.json b/nexxT/core/ConfigFileSchema.json
index 0d9d9a7..16bd489 100644
--- a/nexxT/core/ConfigFileSchema.json
+++ b/nexxT/core/ConfigFileSchema.json
@@ -16,7 +16,7 @@
     "connection": {
       "description": "Used for specifying a connection.",
       "type": "string",
-      "pattern": "^[A-Za-z_][A-Za-z0-9_-]*[.][A-Za-z_][A-Za-z0-9_-]*\\s*[-][>]\\s*[A-Za-z_][A-Za-z0-9_-]*[.][A-Za-z_][A-Za-z0-9_-]*$"
+      "pattern": "^[A-Za-z_][A-Za-z0-9_-]*[.][A-Za-z_][A-Za-z0-9_-]*\\s*[-]\\d*[>]\\s*[A-Za-z_][A-Za-z0-9_-]*[.][A-Za-z_][A-Za-z0-9_-]*$"
     },
     "propertySection": {
       "type": "object",
diff --git a/nexxT/core/Configuration.py b/nexxT/core/Configuration.py
index 42ce6af..a618efb 100644
--- a/nexxT/core/Configuration.py
+++ b/nexxT/core/Configuration.py
@@ -9,7 +9,7 @@
 """
 
 import logging
-from PySide2.QtCore import QObject, Slot, Signal
+from nexxT.Qt.QtCore import QObject, Slot, Signal
 from nexxT.core.Application import Application
 from nexxT.core.CompositeFilter import CompositeFilter
 from nexxT.core.Exceptions import (NexTRuntimeError, CompositeRecursion, NodeNotFoundError, NexTInternalError,
@@ -127,7 +127,7 @@ def compositeLookup(name):
                     raise CompositeRecursion(name)
                 try:
                     return self.compositeFilterByName(name)
-                except NodeNotFoundError:
+                except NodeNotFoundError as exc:
                     recursiveset.add(name)
                     try:
                         for cfg_cf in cfg["composite_filters"]:
@@ -135,7 +135,7 @@ def compositeLookup(name):
                                 cf = CompositeFilter(name, self)
                                 cf.load(cfg_cf, compositeLookup)
                                 return cf
-                        raise NodeNotFoundError("name")
+                        raise NodeNotFoundError("name") from exc
                     finally:
                         recursiveset.remove(name)
 
@@ -210,7 +210,7 @@ def compositeFilterByName(self, name):
             return match[0]
         if len(match) == 0:
             raise NodeNotFoundError(name)
-        raise NexTInternalError("non unique name %s" % name)
+        raise NexTInternalError(f"non unique name {name}")
 
     def applicationByName(self, name):
         """
@@ -223,7 +223,7 @@ def applicationByName(self, name):
             return match[0]
         if len(match) == 0:
             raise NodeNotFoundError(name)
-        raise NexTInternalError("non unique name %s" % name)
+        raise NexTInternalError(f"non unique name {name}")
 
     def subConfigByNameAndTye(self, name, typeid):
         """
@@ -236,7 +236,7 @@ def subConfigByNameAndTye(self, name, typeid):
             return self.applicationByName(name)
         if typeid == self.CONFIG_TYPE_COMPOSITE:
             return self.compositeFilterByName(name)
-        raise NexTInternalError("unknown typeid %s" % typeid)
+        raise NexTInternalError(f"unknown typeid {typeid}")
 
     @Slot(str)
     def activate(self, appname):
@@ -250,7 +250,7 @@ def activate(self, appname):
                 app.activate()
                 self.appActivated.emit(appname, Application.activeApplication)
                 return
-        raise NexTRuntimeError("Application '%s' not found." % appname)
+        raise NexTRuntimeError(f"Application '{appname}' not found.")
 
     @Slot(str, str)
     def renameComposite(self, oldName, newName):
@@ -310,7 +310,7 @@ def addNewApplication(self):
         existing = [a.getName() for a in self._applications]
         while name in existing:
             idx += 1
-            name = "application_%d" % idx
+            name = f"application_{idx}"
         Application(name, self)
         return name
 
@@ -323,7 +323,7 @@ def addNewCompositeFilter(self):
         idx = 1
         while len([c for c in self._compositeFilters if c.getName() == name]) > 0:
             idx += 1
-            name = "composite_%d" % idx
+            name = f"composite_{idx}"
         CompositeFilter(name, self)
         return name
 
@@ -345,7 +345,7 @@ def getCompositeFilterNames(self):
     def _checkUniqueName(collection, name):
         for i in collection:
             if i.getName() == name:
-                raise NexTRuntimeError("Name '%s' is not unique." % name)
+                raise NexTRuntimeError(f"Name '{name}' is not unique.")
 
     def checkRecursion(self):
         """
diff --git a/nexxT/core/Exceptions.py b/nexxT/core/Exceptions.py
index c49d231..a2c5f9a 100644
--- a/nexxT/core/Exceptions.py
+++ b/nexxT/core/Exceptions.py
@@ -10,6 +10,7 @@
 
 def _factoryToString(factory):
     # pylint: disable=import-outside-toplevel
+    # pylint: disable=cyclic-import
     # needed to avoid recursive import
     from nexxT.interface.Ports import InputPortInterface, OutputPortInterface
     if isinstance(factory, str):
@@ -31,56 +32,56 @@ class NodeExistsError(NexTRuntimeError):
     raised node is added or renamed which already exists
     """
     def __init__(self, nodeName):
-        super().__init__("Node %s already exists." % (nodeName))
+        super().__init__(f"Node {nodeName} already exists.")
 
 class NodeNotFoundError(NexTRuntimeError):
     """
     raised node is added or renamed which already exists
     """
     def __init__(self, nodeName):
-        super().__init__("Node %s not found." % (nodeName))
+        super().__init__(f"Node {nodeName} not found.")
 
 class NodeProtectedError(NexTRuntimeError):
     """
     raised if protected node is to be deleted or renamed
     """
     def __init__(self, nodeName):
-        super().__init__("Node %s is protected and cannot be deleted or renamed." % (nodeName))
+        super().__init__(f"Node {nodeName} is protected and cannot be deleted or renamed.")
 
 class PortExistsError(NexTRuntimeError):
     """
     raised when a port is added or renamed which already exists
     """
     def __init__(self, nodeName, portName, factory=None):
-        super().__init__("%sPort %s/%s already exists." % (_factoryToString(factory), nodeName, portName))
+        super().__init__(f"{_factoryToString(factory)}Port {nodeName}/{portName} already exists.")
 
 class PortNotFoundError(NexTRuntimeError):
     """
     raised when a referenced port is not found.
     """
     def __init__(self, nodeName, portName, factory=None):
-        super().__init__("%sPort %s/%s not found." % (_factoryToString(factory), nodeName, portName))
+        super().__init__(f"{_factoryToString(factory)}Port {nodeName}/{portName} not found.")
 
 class DynamicPortUnsupported(NexTRuntimeError):
     """
     raised for unsupported dynamic port operations
     """
     def __init__(self, portName, factory=None):
-        super().__init__("No dynamic %sPort support; port name: %s" % (_factoryToString(factory), portName))
+        super().__init__(f"No dynamic {_factoryToString(factory)}Port support; port name: {portName}")
 
 class ConnectionExistsError(NexTRuntimeError):
     """
     raised when a connection is added twice
     """
     def __init__(self, nodeFrom, portFrom, nodeTo, portTo):
-        super().__init__("Connection from %s/%s to %s/%s already exists." % (nodeFrom, portFrom, nodeTo, portTo))
+        super().__init__(f"Connection from {nodeFrom}/{portFrom} to {nodeTo}/{portTo} already exists.")
 
 class ConnectionNotFound(NexTRuntimeError):
     """
     raised when a connection is added twice
     """
     def __init__(self, nodeFrom, portFrom, nodeTo, portTo):
-        super().__init__("Connection from %s/%s to %s/%s not found." % (nodeFrom, portFrom, nodeTo, portTo))
+        super().__init__(f"Connection from {nodeFrom}/{portFrom} to {nodeTo}/{portTo} not found.")
 
 class UnknownPluginType(NexTRuntimeError):
     """
@@ -98,9 +99,10 @@ class UnexpectedFilterState(NexTRuntimeError):
     """
     def __init__(self, state, operation):
         # pylint: disable=import-outside-toplevel
+        # pylint: disable=cyclic-import
         # needed to avoid recursive import
         from nexxT.interface.Filters import FilterState
-        super().__init__("Operation '%s' cannot be performed in state %s" % (operation, FilterState.state2str(state)))
+        super().__init__(f"Operation '{operation}' cannot be performed in state {FilterState.state2str(state)}")
 
 class FilterStateMachineError(UnexpectedFilterState):
     """
@@ -108,6 +110,7 @@ class FilterStateMachineError(UnexpectedFilterState):
     """
     def __init__(self, oldState, newState):
         # pylint: disable=import-outside-toplevel
+        # pylint: disable=cyclic-import
         # needed to avoid recursive import
         from nexxT.interface.Filters import FilterState
         super().__init__(oldState, "Transition to " + FilterState.state2str(newState))
@@ -117,28 +120,28 @@ class PropertyCollectionChildExists(NexTRuntimeError):
     raised when trying to create an already existing property collection
     """
     def __init__(self, name):
-        super().__init__("PropertyCollection already has a child named %s" % name)
+        super().__init__(f"PropertyCollection already has a child named {name}")
 
 class PropertyCollectionChildNotFound(NexTRuntimeError):
     """
     raised when trying to access a non-existing property collection
     """
     def __init__(self, name):
-        super().__init__("PropertyCollection has no child named %s" % name)
+        super().__init__(f"PropertyCollection has no child named {name}")
 
 class PropertyCollectionPropertyNotFound(NexTRuntimeError):
     """
     raised when trying to set the value of an unknown property
     """
     def __init__(self, name):
-        super().__init__("PropertyCollection has no property named %s" % name)
+        super().__init__(f"PropertyCollection has no property named {name}")
 
 class PropertyCollectionUnknownType(NexTRuntimeError):
     """
     raised when the type given to getProperty or setProperty is unknown to the property system
     """
     def __init__(self, value):
-        super().__init__("PropertyCollection has been provided with an invalid typed value %s" % (repr(value)))
+        super().__init__(f"PropertyCollection has been provided with an invalid typed value {repr(value)}")
 
 class PropertyParsingError(NexTRuntimeError):
     """
@@ -150,25 +153,25 @@ class PropertyInconsistentDefinition(NexTRuntimeError):
     raised when the same property is defined in different ways
     """
     def __init__(self, name):
-        super().__init__("Inconsistent definitions for property named %s" % name)
+        super().__init__(f"Inconsistent definitions for property named {name}")
 
 class InvalidIdentifierException(NexTRuntimeError):
     """
     raised when a provided name doesn't confirm to identifier specification
     """
     def __init__(self, name):
-        super().__init__("Invalid identifier '%s'" % name)
+        super().__init__(f"Invalid identifier '{name}'")
 
 class CompositeRecursion(NexTRuntimeError):
     """
     raised when a composite filter is dependent on itself
     """
     def __init__(self, name):
-        super().__init__("Composite filter '%s' depends on itself." % name)
+        super().__init__(f"Composite filter '{name}' depends on itself.")
 
 class PossibleDeadlock(NexTRuntimeError):
     """
     raised during application activation when a possible deadlock is detected (a cycle was found in the thread graph)
     """
     def __init__(self, cycle):
-        super().__init__("This graph is not deadlock-safe. A cycle has been found in the thread graph: %s" % cycle)
+        super().__init__(f"This graph is not deadlock-safe. A cycle has been found in the thread graph: {cycle}")
diff --git a/nexxT/core/FilterEnvironment.py b/nexxT/core/FilterEnvironment.py
index 290f867..e6c5859 100644
--- a/nexxT/core/FilterEnvironment.py
+++ b/nexxT/core/FilterEnvironment.py
@@ -10,7 +10,7 @@
 
 import copy
 import logging
-from PySide2.QtCore import Signal, QMutex, QMutexLocker
+from nexxT.Qt.QtCore import Signal, QRecursiveMutex, QMutexLocker
 from nexxT.interface import FilterState, InputPortInterface, OutputPortInterface
 from nexxT import useCImpl
 from nexxT.core.BaseFilterEnvironment import BaseFilterEnvironment
@@ -32,7 +32,7 @@ class FilterEnvironment(BaseFilterEnvironment): # pylint: disable=too-many-publi
     def __init__(self, library, factoryFunction, propertyCollection, mockup=None):
         BaseFilterEnvironment.__init__(self, propertyCollection)
         # ports are accessed by multiple threads (from FilterMockup.createFilter)
-        self._portMutex = QMutex(QMutex.Recursive)
+        self._portMutex = QRecursiveMutex()
         self._ports = []
         self._mockup = mockup
         self._state = FilterState.CONSTRUCTING
@@ -84,6 +84,7 @@ def guiState(self):
         :return:
         """
         # pylint: disable=import-outside-toplevel
+        # pylint: disable=cyclic-import
         # to avoid recursive import
         from nexxT.core.Thread import NexTThread
         from nexxT.core.Application import Application
@@ -102,6 +103,7 @@ def getFullQualifiedName(self):
         :return: a string instance.
         """
         # pylint: disable=import-outside-toplevel
+        # pylint: disable=cyclic-import
         # to avoid recursive import
         from nexxT.core.Thread import NexTThread
         from nexxT.core.Application import Application
@@ -274,7 +276,7 @@ def preStateTransition(self, operation):
             FilterState.DEINITIALIZING: (FilterState.INITIALIZED, FilterState.CONSTRUCTED, self.getPlugin().onDeinit),
             FilterState.DESTRUCTING: (FilterState.CONSTRUCTED, None, None),
         }
-        fromState, toState, function = operations[operation]
+        fromState, _, _ = operations[operation]
         if self._state != fromState:
             raise FilterStateMachineError(self._state, operation)
         self._state = operation
@@ -306,7 +308,7 @@ def _stateTransition(self, operation):
         self._state = operation
         try:
             function()
-        except Exception as e: # pylint: disable=broad-except
+        except Exception: # pylint: disable=broad-except
             # What should be done on errors?
             #    1. inhibit state transition to higher state
             #         pro: prevent activation of not properly intialized filter
diff --git a/nexxT/core/FilterMockup.py b/nexxT/core/FilterMockup.py
index 86d23b0..8bac493 100644
--- a/nexxT/core/FilterMockup.py
+++ b/nexxT/core/FilterMockup.py
@@ -9,7 +9,7 @@
 """
 
 import logging
-from PySide2.QtCore import QMutexLocker, Qt
+from nexxT.Qt.QtCore import QMutexLocker, Qt
 from nexxT.interface import InputPort, OutputPort, InputPortInterface, OutputPortInterface
 from nexxT.core.FilterEnvironment import FilterEnvironment
 from nexxT.core.PropertyCollectionImpl import PropertyCollectionImpl
@@ -101,10 +101,11 @@ def _createFilterAndUpdate(self):
             self._pluginClass = tempEnv.getPlugin().__class__
             if nexxT.useCImpl:
                 # pylint: disable=import-outside-toplevel
+                # pylint: disable=cyclic-import
                 # don't want to pollute global namespace with this; need to update the
                 # the class if it is a wrapped shared pointer.
-                import cnexxT
-                if self._pluginClass is cnexxT.__dict__["QSharedPointer<nexxT::Filter >"]:
+                import cnexxT # pylint: disable=import-error
+                if self._pluginClass is cnexxT.QSharedPointer_nexxT_Filter:
                     self._pluginClass = tempEnv.getPlugin().data().__class__
 
     def createFilter(self):
diff --git a/nexxT/core/Graph.py b/nexxT/core/Graph.py
index 03490b6..0a92c9a 100644
--- a/nexxT/core/Graph.py
+++ b/nexxT/core/Graph.py
@@ -9,7 +9,7 @@
 """
 
 import logging
-from PySide2.QtCore import Signal, Slot
+from nexxT.Qt.QtCore import Signal, Slot
 from nexxT.interface import InputPortInterface, OutputPortInterface
 from nexxT.core.FilterMockup import FilterMockup
 from nexxT.core.BaseGraph import BaseGraph
@@ -32,7 +32,7 @@ class FilterGraph(BaseGraph):
     dynOutputPortDeleted = Signal(str, str)
 
     def __init__(self, subConfig):
-        super().__init__()
+        super().__init__(defaultConnProp=dict(width=1))
         assertMainThread()
         self._parent = subConfig
         self._filters = {}
@@ -57,7 +57,7 @@ def nodeName(self, filterEnv):
         :param filterEnv: a FilterEnvironment instance
         :return: the name as string
         """
-        names = [n for n in self._filters if self._filters[n] is filterEnv]
+        names = [n for n,f in self._filters.items() if f is filterEnv]
         if len(names) != 1:
             raise NexTRuntimeError("Lookup of filter failed; either non-unique or not in graph.")
         return names[0]
@@ -263,9 +263,9 @@ def _portInformationUpdated(self, oldIn, oldOut):
         assertMainThread()
         def _nodeName():
             fe = self.sender()
-            name = [k for k in self._filters if self._filters[k] is fe]
+            name = [k for k, f in self._filters.items() if f is fe]
             if len(name) != 1:
-                raise RuntimeError("Unexpected list length of matched filters: %s" % (name))
+                raise RuntimeError(f"Unexpected list length of matched filters: {name}")
             return name[0]
 
         name = _nodeName()
diff --git a/nexxT/core/PluginManager.py b/nexxT/core/PluginManager.py
index aad5aa8..f1a9b4a 100644
--- a/nexxT/core/PluginManager.py
+++ b/nexxT/core/PluginManager.py
@@ -17,7 +17,7 @@
 import importlib.util
 from importlib.machinery import ExtensionFileLoader, EXTENSION_SUFFIXES
 import pkg_resources
-from PySide2.QtCore import QObject
+from nexxT.Qt.QtCore import QObject
 from nexxT.core.Exceptions import UnknownPluginType, NexTRuntimeError, PluginException
 from nexxT.interface import Filter, FilterSurrogate
 from nexxT.core import PluginInterface
@@ -36,7 +36,7 @@ def __init__(self, library):
     def __getattr__(self, attr):
         if attr in self._availableFilterTypes:
             return lambda env, library=self._library: PluginInterface.singleton().create(library, attr, env)
-        raise NexTRuntimeError("requested creation func '%s' not found in %s" % (attr, self._library))
+        raise NexTRuntimeError(f"requested creation func '{attr}' not found in {self._library}")
 
     def __getitem__(self, idx):
         return self._availableFilterTypes[idx]
@@ -63,7 +63,8 @@ class PythonLibrary:
     LIBTYPE_ENTRY_POINT = 2
 
     # blacklisted packages are not unloaded when closing an application.
-    BLACKLISTED_PACKAGES = ["h5py", "numpy", "matplotlib", "PySide2", "shiboken2", "torch", "tf"]
+    BLACKLISTED_PACKAGES = ["h5py", "numpy", "matplotlib", "nexxT.Qt", "PySide6",
+                            "nexxT.shiboken", "shiboken2", "shiboken6", "torch", "tf"]
 
     def __init__(self, library, libtype):
         self._library = library
@@ -73,7 +74,7 @@ def __init__(self, library, libtype):
             PythonLibrary._pyLoadCnt += 1
             logging.getLogger(__name__).debug("importing python module from file '%s'", library)
             # https://stackoverflow.com/questions/67631/how-to-import-a-module-given-the-full-path
-            spec = importlib.util.spec_from_file_location("nexxT.plugins.plugin%d" % PythonLibrary._pyLoadCnt, library)
+            spec = importlib.util.spec_from_file_location(f"nexxT.plugins.plugin{PythonLibrary._pyLoadCnt}", library)
             self._mod = importlib.util.module_from_spec(spec)
             spec.loader.exec_module(self._mod)
         elif self._libtype == self.LIBTYPE_MODULE:
@@ -88,7 +89,7 @@ def __init__(self, library, libtype):
             if len(found) > 1:
                 logging.getLogger(__name__).warning("found more than one entry points named '%s'", library)
             if len(found) == 0:
-                raise ModuleNotFoundError("Entry point '%s' not found." % (library))
+                raise ModuleNotFoundError(f"Entry point '{library}' not found.")
             self._mod = found[0].load()
         modulesAfter = set(sys.modules.keys())
         self._loadedModules = modulesAfter.difference(modulesBefore)
@@ -106,7 +107,7 @@ def __getattr__(self, attr):
             return getattr(PluginManager.singleton().getLibrary(dll), res.name())
         if res is not None:
             return res
-        raise NexTRuntimeError("requested creation func '%s' not found in %s" % (attr, self._library))
+        raise NexTRuntimeError(f"requested creation func '{attr}' not found in {self._library}")
 
     def _checkAvailableFilters(self):
         if self._libtype == self.LIBTYPE_ENTRY_POINT:
@@ -220,8 +221,8 @@ def create(self, library, factoryFunction, filterEnvironment):
                     # catching a general exception is exactly what is wanted here
                     logging.getLogger(__name__).exception("Exception while creating %s from library '%s'",
                                                           factoryFunction, library)
-                    raise PluginException("Unexpected exception while loading the plugin %s:%s (%s)" %
-                                          (library, factoryFunction, e))
+                    raise PluginException(
+                        f"Unexpected exception while loading the plugin {library}:{factoryFunction} ({e})") from e
             res = getattr(self._libraries[library], factoryFunction)(filterEnvironment)
         else:
             res = getattr(library, factoryFunction)(filterEnvironment)
@@ -252,10 +253,8 @@ def getLibrary(self, library):
                 raise
             except Exception as e: # pylint: disable=broad-except
                 # catching a general exception is exactly what is wanted here
-                logging.getLogger(__name__).exception("Exception while loading library '%s'",
-                                                      library)
-                raise PluginException("Unexpected exception while loading the library %s (%s)" %
-                                      (library, e))
+                logging.getLogger(__name__).exception("Exception while loading library '%s'", library)
+                raise PluginException(f"Unexpected exception while loading the library {library} ({e})") from e
         lib = self._libraries[library]
         return lib
 
@@ -280,7 +279,7 @@ def _load(self, library):
             return self._loadEntryPoint(library[len("entry_point://"):])
         if library.startswith("binary://"):
             return self._loadBinary(library[len("binary://"):], self._prop)
-        raise UnknownPluginType("don't know how to load library '%s'" % library)
+        raise UnknownPluginType(f"don't know how to load library '{library}'")
 
     @staticmethod
     def _loadPyfile(library, prop=None):
diff --git a/nexxT/core/PortImpl.py b/nexxT/core/PortImpl.py
index 8624c90..3868e91 100644
--- a/nexxT/core/PortImpl.py
+++ b/nexxT/core/PortImpl.py
@@ -9,7 +9,7 @@
 """
 
 import logging
-from PySide2.QtCore import QThread, QSemaphore, Signal, QObject, Qt
+from nexxT.Qt.QtCore import QThread, QSemaphore, Signal, QObject, Qt
 from nexxT.interface.Ports import InputPortInterface, OutputPortInterface
 from nexxT.interface.DataSamples import DataSample
 from nexxT.interface.Services import Services
@@ -24,10 +24,10 @@ class InterThreadConnection(QObject):
     """
     transmitInterThread = Signal(object, QSemaphore)
 
-    def __init__(self, qthread_from):
+    def __init__(self, qthreadFrom, width):
         super().__init__()
-        self.moveToThread(qthread_from)
-        self.semaphore = QSemaphore(1)
+        self.moveToThread(qthreadFrom)
+        self.semaphore = QSemaphore(width) if width > 0 else None
         self._stopped = True
 
     def receiveSample(self, dataSample):
@@ -45,7 +45,7 @@ def _receiveSample(self, dataSample):
             if self._stopped:
                 logger.info("The inter-thread connection is set to stopped mode; data sample discarded.")
                 break
-            if self.semaphore.tryAcquire(1, 500):
+            if self.semaphore is None or self.semaphore.tryAcquire(1, 500):
                 self.transmitInterThread.emit(dataSample, self.semaphore)
                 break
 
@@ -95,24 +95,25 @@ def setupDirectConnection(outputPort, inputPort):
 
         :param outputPort: the output port instance to be connected
         :param inputPort: the input port instance to be connected
-        :return:None
+        :return: None
         """
         logger.info("setup direct connection between %s -> %s", outputPort.name(), inputPort.name())
         outputPort.transmitSample.connect(inputPort.receiveSync, Qt.DirectConnection)
 
     @staticmethod
-    def setupInterThreadConnection(outputPort, inputPort, outputPortThread):
+    def setupInterThreadConnection(outputPort, inputPort, outputPortThread, width):
         """
         Setup an inter thread connection between outputPort and inputPort
 
         :param outputPort: the output port instance to be connected
         :param inputPort: the input port instance to be connected
         :param outputPortThread: the QThread instance of the outputPort instance
+        :param width: the width of the connection in DataSamples (0: infinite)
         :return: an InterThreadConnection instance which manages the connection (has
                  to survive until connections is deleted)
         """
         logger.info("setup inter thread connection between %s -> %s", outputPort.name(), inputPort.name())
-        itc = InterThreadConnection(outputPortThread)
+        itc = InterThreadConnection(outputPortThread, width)
         outputPort.transmitSample.connect(itc.receiveSample, Qt.DirectConnection)
         itc.transmitInterThread.connect(inputPort.receiveAsync, Qt.QueuedConnection)
         return itc
@@ -206,10 +207,11 @@ def _receiveAsync(self, dataSample, semaphore):
         if not QThread.currentThread() is self.thread():
             raise NexTInternalError("InputPort.receiveAsync has been called from an unexpected thread.")
         self._addToQueue(dataSample)
-        if not self._interthreadDynamicQueue:
+        if not self._interthreadDynamicQueue or semaphore is None:
             # usual behaviour
             self._transmit()
-            semaphore.release(1)
+            if semaphore is not None:
+                semaphore.release(1)
         else:
             if semaphore not in self._semaphoreN:
                 self._semaphoreN[semaphore] = 1
@@ -224,7 +226,7 @@ def _receiveAsync(self, dataSample, semaphore):
                 # first acquire is done by caller
                 self._semaphoreN[semaphore] -= 1
                 # the semaphore's N is too large, try acquires to reduce the size
-                for i in range(1, delta):
+                for _ in range(1, delta):
                     if semaphore.tryAcquire(1):
                         self._semaphoreN[semaphore] -= 1
                     else:
@@ -303,6 +305,7 @@ def setInterthreadDynamicQueue(self, enabled):
         if enabled != self._interthreadDynamicQueue:
             state = self.environment().state()
             # pylint: disable=import-outside-toplevel
+            # pylint: disable=cyclic-import
             # needed to avoid recursive import
             from nexxT.interface.Filters import FilterState # avoid recursive import
             if state not in [FilterState.CONSTRUCTING, FilterState.CONSTRUCTED,
diff --git a/nexxT/core/PropertyCollectionImpl.py b/nexxT/core/PropertyCollectionImpl.py
index af9e1d4..64720cf 100644
--- a/nexxT/core/PropertyCollectionImpl.py
+++ b/nexxT/core/PropertyCollectionImpl.py
@@ -14,8 +14,8 @@
 import platform
 import string
 import os
-import shiboken2
-from PySide2.QtCore import Signal, Slot, QMutex, QMutexLocker
+import nexxT.shiboken
+from nexxT.Qt.QtCore import Signal, Slot, QRecursiveMutex, QMutexLocker
 from nexxT.core.Exceptions import (PropertyCollectionChildNotFound, PropertyCollectionChildExists,
                                    PropertyParsingError, NexTInternalError,
                                    PropertyInconsistentDefinition, PropertyCollectionPropertyNotFound)
@@ -54,7 +54,7 @@ def __init__(self, name, parentPropColl, loadedFromConfig=None):
         self._properties = {}
         self._accessed = False # if no access to properties has been made, we stick with configs from config file.
         self._loadedFromConfig = loadedFromConfig if loadedFromConfig is not None else {}
-        self._propertyMutex = QMutex(QMutex.Recursive)
+        self._propertyMutex = QRecursiveMutex()
         if parentPropColl is not None:
             if not isinstance(parentPropColl, PropertyCollectionImpl):
                 raise NexTInternalError("parentPropColl should always be a property collection instance but it isn't")
@@ -72,6 +72,11 @@ def applyLoadedConfig(self, loadedFromConfig):
         self._loadedFromConfig = loadedFromConfig
 
     def childEvent(self, event):
+        """
+        Overwritten from QObject
+
+        :param event: a QChildEvent instance.
+        """
         assertMainThread()
         if event.added():
             self.childAdded.emit(self, event.child().objectName())
@@ -124,6 +129,7 @@ def defineProperty(self, name, defaultVal, helpstr, options=None, propertyHandle
             assert isinstance(propertyHandler, PropertyHandler)
             assert isinstance(options, dict)
             if propertyHandler.validate(defaultVal) != defaultVal:
+                # pylint: disable=consider-using-f-string
                 raise PropertyInconsistentDefinition(
                     "The validation of the default value must be the identity (%s != %s)!" %
                     (repr(propertyHandler.validate(defaultVal)), repr(defaultVal)))
@@ -135,8 +141,8 @@ def defineProperty(self, name, defaultVal, helpstr, options=None, propertyHandle
                     try:
                         p.value = p.handler.validate(p.handler.fromConfig(l))
                     except Exception as e:
-                        raise PropertyParsingError("Error parsing property %s from %s (original exception: %s)" %
-                                                   (name, repr(l), str(e)))
+                        raise PropertyParsingError(
+                            f"Error parsing property {name} from {repr(l)} (original exception: {str(e)})") from e
                 self.propertyAdded.emit(self, name)
             else:
                 # the arguments to getProperty shall be consistent among calls
@@ -198,8 +204,8 @@ def setProperty(self, name, value):
             try:
                 value = p.handler.validate(value)
             except Exception as e:
-                raise PropertyParsingError("Error parsing property %s from %s (original exception: %s)" %
-                                           (name, repr(value), str(e)))
+                raise PropertyParsingError(
+                    f"Error parsing property {name} from {repr(value)} (original exception: {str(e)})") from e
             if value != p.value:
                 p.value = value
                 self.propertyChanged.emit(self, name)
@@ -210,8 +216,8 @@ def markAllUnused(self):
         :return: None
         """
         with QMutexLocker(self._propertyMutex):
-            for n in self._properties:
-                self._properties[n].used = False
+            for _, p in self._properties.items():
+                p.used = False
 
     def deleteUnused(self):
         """
@@ -223,8 +229,8 @@ def deleteUnused(self):
             return
         with QMutexLocker(self._propertyMutex):
             toDel = []
-            for n in self._properties:
-                if not self._properties[n].used:
+            for n, p in self._properties.items():
+                if not p.used:
                     toDel.append(n)
             for n in toDel:
                 del self._properties[n]
@@ -290,8 +296,8 @@ def deleteChild(self, name):
         for c in cc.children():
             if isinstance(c, PropertyCollectionImpl):
                 cc.deleteChild(c.objectName())
-        if shiboken2.isValid(cc): # pylint: disable=no-member
-            shiboken2.delete(cc) # pylint: disable=no-member
+        if nexxT.shiboken.isValid(cc): # pylint: disable=no-member
+            nexxT.shiboken.delete(cc) # pylint: disable=no-member
 
     def evalpath(self, path):
         """
@@ -308,9 +314,9 @@ def evalpath(self, path):
             NEXXT_VARIANT="release"
         )
         if platform.system() == "Windows":
-            default_environ["NEXXT_PLATFORM"] = "msvc_x86%s" % ("_64" if platform.architecture()[0] == "64bit" else "")
+            default_environ["NEXXT_PLATFORM"] = f"msvc_x86{'_64' if platform.architecture()[0] == '64bit' else ''}"
         else:
-            default_environ["NEXXT_PLATFORM"] = "linux_x86%s" % ("_64" if platform.architecture()[0] == "64bit" else "")
+            default_environ["NEXXT_PLATFORM"] = f"linux_x86{'_64' if platform.architecture()[0] == '64bit' else ''}"
         origpath = path
         path = string.Template(path).safe_substitute({**default_environ, **os.environ})
         logger.debug("interpolated path %s -> %s", origpath, path)
diff --git a/nexxT/core/PropertyHandlers.py b/nexxT/core/PropertyHandlers.py
index 302cb0b..35e8f97 100644
--- a/nexxT/core/PropertyHandlers.py
+++ b/nexxT/core/PropertyHandlers.py
@@ -9,7 +9,7 @@
 """
 
 import logging
-from PySide2.QtWidgets import QSpinBox, QLineEdit, QComboBox
+from nexxT.Qt.QtWidgets import QSpinBox, QLineEdit, QComboBox
 from nexxT.interface import PropertyHandler
 from nexxT.core.Exceptions import PropertyParsingError, PropertyCollectionUnknownType
 
@@ -29,9 +29,9 @@ def __init__(self, options):
         for k in options:
             if k in ["min", "max"]:
                 if not isinstance(options[k], int):
-                    raise PropertyParsingError("Unexpected type of option %s; expected int." % k)
+                    raise PropertyParsingError(f"Unexpected type of option {k}; expected int.")
             else:
-                raise PropertyParsingError("Unexpected option %s; expected 'min' or 'max'." % k)
+                raise PropertyParsingError(f"Unexpected option {k}; expected 'min' or 'max'.")
         self._options = options
 
     def options(self):
@@ -144,7 +144,7 @@ def __init__(self, options):
                     if not isinstance(v, str):
                         raise PropertyParsingError("enum options must be defined as list of strings.")
             else:
-                raise PropertyParsingError("Unknown option %s for string properties" % k)
+                raise PropertyParsingError(f"Unknown option {k} for string properties")
         self._options = options
 
     def options(self):
@@ -239,9 +239,9 @@ def __init__(self, options):
         for k in options:
             if k in ["min", "max"]:
                 if not isinstance(options[k], int) and not isinstance(options[k], float):
-                    raise PropertyParsingError("Unexpected type of option %s; expected int." % k)
+                    raise PropertyParsingError(f"Unexpected type of option {k}; expected int.")
             else:
-                raise PropertyParsingError("Unexpected option %s; expected 'min' or 'max'." % k)
+                raise PropertyParsingError(f"Unexpected option {k}; expected 'min' or 'max'.")
         self._options = options
 
     def options(self):
@@ -330,7 +330,7 @@ class BoolHandler(PropertyHandler):
 
     def __init__(self, options):
         for k in options:
-            raise PropertyParsingError("Unexpected option %s; expected 'min' or 'max'." % k)
+            raise PropertyParsingError(f"Unexpected option {k}; expected 'min' or 'max'.")
         self._options = options
 
     def options(self):
@@ -420,4 +420,4 @@ def defaultHandler(propertyValue):
         return StringHandler
     if isinstance(propertyValue, float):
         return FloatHandler
-    raise PropertyCollectionUnknownType("Cannot deduce default handler for property value %s" % repr(propertyValue))
+    raise PropertyCollectionUnknownType(f"Cannot deduce default handler for property value {repr(propertyValue)}")
diff --git a/nexxT/core/SubConfiguration.py b/nexxT/core/SubConfiguration.py
index 3e2b898..4ff65f2 100644
--- a/nexxT/core/SubConfiguration.py
+++ b/nexxT/core/SubConfiguration.py
@@ -8,9 +8,10 @@
 This module defines the class SubConfiguration
 """
 
-import logging
 from collections import OrderedDict
-from PySide2.QtCore import QObject, Signal
+import logging
+import re
+from nexxT.Qt.QtCore import QObject, Signal
 from nexxT.core.Graph import FilterGraph
 from nexxT.core.PropertyCollectionImpl import PropertyCollectionImpl
 from nexxT.core.Exceptions import NexTInternalError, PropertyCollectionPropertyNotFound, PropertyCollectionChildNotFound
@@ -89,22 +90,35 @@ def getConfiguration(self):
     def connectionStringToTuple(con):
         """
         Converts a connection string to a 4 tuple.
+
         :param con: a string containing a connection "name1.port1 -> name2.port2"
-        :return: a 4-tuple ("name1", "port1", "name2", "port2")
+                    or also "name1.port1 -[int]> name2.port2"
+        :return: a 5-tuple ("name1", "port1", "name2", "port2", connwidth)
         """
-        f, t = con.split("->")
-        fromNode, fromPort = f.strip().split(".")
-        toNode, toPort = t.strip().split(".")
-        return fromNode.strip(), fromPort.strip(), toNode.strip(), toPort.strip()
+        if "->" in con:
+            f, t = con.split("->")
+            fromNode, fromPort = f.strip().split(".")
+            toNode, toPort = t.strip().split(".")
+            width = 1
+        else:
+            match = re.match(r"^(.+)\.(.+)\s*-(\d)+>\s*(.+)\.(.+)$", con)
+            if match is None:
+                raise RuntimeError(f"Cannot parse connection string '{con}'")
+            fromNode, fromPort, width, toNode, toPort = match.groups()
+            width = int(width)
+        return fromNode.strip(), fromPort.strip(), toNode.strip(), toPort.strip(), width
 
     @staticmethod
     def tupleToConnectionString(connection):
         """
         Converts a 4-tuple connection to a string.
-        :param connection: a 4-tuple ("name1", "port1", "name2", "port2")
-        :return: a string "name1.port1 -> name2.port2"
+        :param connection: a 4-tuple ("name1", "port1", "name2", "port2", width)
+        :return: a string "name1.port1 -1> name2.port2"
         """
-        return "%s.%s -> %s.%s" % connection
+        fromPort, fromNode, toPort, toNode, width = connection
+        if width == 1:
+            return f"{fromPort}.{fromNode} -> {toPort}.{toNode}"
+        return f"{fromPort}.{fromNode} -{width}> {toPort}.{toNode}"
 
     def load(self, cfg, compositeLookup):
         """
@@ -121,7 +135,7 @@ def load(self, cfg, compositeLookup):
             if not n["library"].startswith("composite://"):
                 p = PropertyCollectionImpl(n["name"], self._propertyCollection, n["properties"])
                 # apply node gui state
-                nextP = PropertyCollectionImpl("_nexxT", p, {"thread": n["thread"]})
+                PropertyCollectionImpl("_nexxT", p, {"thread": n["thread"]})
                 logger.debug("loading: subconfig %s / node %s -> thread: %s", self._name, n["name"], n["thread"])
                 tmp = self._graph.addNode(n["library"], n["factoryFunction"], suggestedName=n["name"],
                                           dynamicInputPorts=n["dynamicInputPorts"],
@@ -148,7 +162,8 @@ def load(self, cfg, compositeLookup):
             self._graph.getMockup(n["name"]).createFilterAndUpdate()
         for c in cfg["connections"]:
             contuple = self.connectionStringToTuple(c)
-            self._graph.addConnection(*contuple)
+            self._graph.addConnection(*contuple[:-1])
+            self._graph.setConnectionProperties(*contuple[:-1], {"width": contuple[-1]})
 
     def save(self):
         """
@@ -164,7 +179,7 @@ def adaptLibAndFactory(lib, factory):
                     ncfg["library"] = "composite://ref"
                     ncfg["factoryFunction"] = lib.getName()
                 else:
-                    raise NexTInternalError("Unexpected factory function '%s'" % factory)
+                    raise NexTInternalError(f"Unexpected factory function '{factory}'")
             else:
                 ncfg["library"] = lib
                 ncfg["factoryFunction"] = factory
@@ -210,7 +225,10 @@ def adaptLibAndFactory(lib, factory):
                 pass
             ncfg["properties"] = p.saveDict()
             cfg["nodes"].append(ncfg)
-        cfg["connections"] = [self.tupleToConnectionString(c) for c in self._graph.allConnections()]
+        cfg["connections"] = []
+        for c in self._graph.allConnections():
+            ct = c + (self._graph.getConnectionProperties(*c)["width"],)
+            cfg["connections"].append(self.tupleToConnectionString(ct))
         return cfg
 
     @staticmethod
@@ -222,6 +240,7 @@ def getThreadSet(item):
         :return: set of strings
         """
         # pylint: disable=import-outside-toplevel
+        # pylint: disable=cyclic-import
         # needed to avoid recursive import
         from nexxT.core.CompositeFilter import CompositeFilter
         from nexxT.core.FilterMockup import FilterMockup
diff --git a/nexxT/core/Thread.py b/nexxT/core/Thread.py
index 1e79c85..3e196ce 100644
--- a/nexxT/core/Thread.py
+++ b/nexxT/core/Thread.py
@@ -11,7 +11,7 @@
 import logging
 import sys
 import threading
-from PySide2.QtCore import QObject, Signal, Slot, QCoreApplication, QThread
+from nexxT.Qt.QtCore import QObject, Signal, Slot, QCoreApplication, QThread
 from nexxT.interface import FilterState, Services
 from nexxT.core.Exceptions import NodeExistsError, NexTInternalError, NodeNotFoundError, NexTRuntimeError
 from nexxT.core.Utils import handleException
@@ -29,7 +29,7 @@ class ThreadWithCoverage(QThread):
         """
 
         @handleException
-        def startupHook(self): # pylint: disable=no-self-use
+        def startupHook(self):
             """
             see https://github.com/nedbat/coveragepy/issues/582
             and https://github.com/nedbat/coveragepy/issues/686
@@ -110,8 +110,8 @@ def cleanup(self):
             self._qthread.wait()
             self._qthread = None
         logger.internal("cleanup filters")
-        for name in self._filters:
-            self._filters[name].destroy()
+        for _, f in self._filters.items():
+            f.destroy()
         self._filters.clear()
         self._filter2name.clear()
         logger.internal("cleanup mockups")
@@ -179,10 +179,10 @@ def performOperation(self, operation, barrier):
             # wait for all threads
             barrier.wait()
         # perform operation for all filters
-        for name in self._mockups:
+        for name, mockup in self._mockups.items():
             try:
                 if operation == "create":
-                    res = self._mockups[name].createFilter()
+                    res = mockup.createFilter()
                     res.setParent(self)
                     self._filters[name] = res
                     self._filter2name[res] = name
diff --git a/nexxT/core/Utils.py b/nexxT/core/Utils.py
index b23ff2b..bf7ec28 100644
--- a/nexxT/core/Utils.py
+++ b/nexxT/core/Utils.py
@@ -16,10 +16,10 @@
 import os.path
 import sqlite3
 import time
-from PySide2.QtCore import (QObject, Signal, Slot, QMutex, QWaitCondition, QCoreApplication, QThread,
+from nexxT.Qt.QtCore import (QObject, Signal, Slot, QMutex, QWaitCondition, QCoreApplication, QThread,
                             QMutexLocker, QRecursiveMutex, QTimer, Qt, QPoint)
-from PySide2.QtGui import QColor, QPainter, QTextLayout, QTextOption
-from PySide2.QtWidgets import QFrame, QSizePolicy
+from nexxT.Qt.QtGui import QColor, QPainter, QTextLayout, QTextOption
+from nexxT.Qt.QtWidgets import QFrame, QSizePolicy
 from nexxT.core.Exceptions import NexTInternalError, InvalidIdentifierException
 
 logger = logging.getLogger(__name__)
@@ -221,7 +221,7 @@ def __init__(self, filename, threadSafety=ONE_CONNECTION_PER_THREAD):
             self.mutex = QRecursiveMutex()
             self.dbs = {}
         else:
-            raise RuntimeError("Unknown threadSafety option %s" % repr(self.threadSafety))
+            raise RuntimeError(f"Unknown threadSafety option {repr(self.threadSafety)}")
 
     def _getDB(self):
         if self.threadSafety == self.SINGLE_CONNECTION:
@@ -269,11 +269,11 @@ def emit(self, record):
 class QByteArrayBuffer(io.IOBase):
     """
     Efficient IOBase wrapper around QByteArray for pythonic access, for memoryview doesn't seem
-    supported; note this seems to have changed in PySide2 5.14.2.
+    supported; note this seems to have changed in nexxT.Qt 5.14.2.
     """
     def __init__(self, qByteArray):
         super().__init__()
-        logger.warning("Using deprecated class QByteArrayBuffer. Since PySide2 5.14.2 you can cast QByteArrays directly"
+        logger.warning("Using deprecated class QByteArrayBuffer. Since PySide6 you can cast QByteArrays directly"
                        "to memoryview and this class is not needed anymore.")
         self._ba = qByteArray
         self._ptr = 0
@@ -341,7 +341,7 @@ def excepthook(*args):
 def handleException(func):
     """
     Can be used as decorator to enable generic exception catching. Important: Do not use
-    this for PySide2 slots because it confuses the PySide2 slot/thread detection logic.
+    this for nexxT.Qt slots because it confuses the nexxT.Qt slot/thread detection logic.
     Instead, make a non-slot method with exception handling and call that method from
     the slot.
     :param func: The function to be wrapped
@@ -353,6 +353,7 @@ def wrapper(*args, **kwargs):
         except Exception: # pylint: disable=broad-except
             # catching a general exception is exactly wanted here
             excepthook(*sys.exc_info())
+            return None
     return wrapper
 
 class ElidedLabel(QFrame):
@@ -451,33 +452,3 @@ def _paintEvent(self, event):
                 painter.drawText(QPoint(0, y + fontMetrics.ascent()), elidedLastLine)
                 break
         textLayout.endLayout()
-
-if __name__ == "__main__": # pragma: no cover
-    def _smokeTestBarrier():
-        # pylint: disable=import-outside-toplevel
-        # pylint: disable=missing-class-docstring
-        import random
-
-        n = 10
-
-        barrier = Barrier(n)
-
-        def threadWork():
-            st = random.randint(0, 5000)/1000.
-            time.sleep(st)
-            barrier.wait()
-
-        class MyThread(QThread):
-            def run(self):
-                threadWork()
-
-        threads = []
-        for _ in range(n):
-            t = MyThread()
-            t.start()
-            threads.append(t)
-
-        for t in threads:
-            t.wait()
-
-    _smokeTestBarrier()
diff --git a/nexxT/core/__init__.py b/nexxT/core/__init__.py
index 51197ed..0898a33 100644
--- a/nexxT/core/__init__.py
+++ b/nexxT/core/__init__.py
@@ -14,6 +14,8 @@
 if nexxT.useCImpl:
     import cnexxT
     PluginInterface = cnexxT.nexxT.PluginInterface
+    Compatibility = cnexxT.nexxT.Compatibility
 else:
     PluginInterface = None
+    Compatibility = None
     
\ No newline at end of file
diff --git a/nexxT/core/qrc_resources.py b/nexxT/core/qrc_resources.py
index 683caf5..4d04c73 100644
--- a/nexxT/core/qrc_resources.py
+++ b/nexxT/core/qrc_resources.py
@@ -1,9 +1,9 @@
 # Resource object code (Python 3)
 # Created by: object code
-# Created by: The Resource Compiler for Qt version 5.15.1
+# Created by: The Resource Compiler for Qt version 6.4.0
 # WARNING! All changes made in this file will be lost!
 
-from PySide2 import QtCore
+from PySide6 import QtCore
 
 qt_resource_data = b"\
 \x00\x00\x08d\
@@ -143,104 +143,104 @@
     y=\x2227.309361\
 \x22 />\x0a  </g>\x0a</sv\
 g>\x0a\
-\x00\x00\x05\xf9\
+\x00\x00\x05\xf5\
 \x00\
-\x00\x1f#x\x9c\xedY[o\xdb6\x14~\xcf\xaf\xd0\
-\x94\x87\xb6X)\x91\xbaK\xb1]l+\x0a\x14\x18\xf6\
-\xb0e\xd83-\xd16\x11I4(:\xb6\xfb\xebw\
-H\xebb\xf92d\x08:\x04\x83\x0d\x04\xb1\xce\x95\xe7\
-\x9c\x8f\x9f\xc8d\xf2iW\x95\xd63\x93\x0d\x17\xf5\xd4\
-&\x0e\xb6-V\xe7\xa2\xe0\xf5rj\xff\xf9\xf8\x05%\
-\xb6\xd5(Z\x17\xb4\x145\x9b\xda\xb5\xb0?\xcd\xee&\
-? d\xfd\x22\x19U\xac\xb0\xb6\x5c\xad\xac\xaf\xf5S\
-\x93\xd35\xb3\xde\xaf\x94Zg\xae\xbb\xddn\x1d\xde\x0a\
-\x1d!\x97\xee\x07\x0b\xa1\xd9\xdd\xdd\xa4y^\xdeY\x96\
-\x05y\xeb&+\xf2\xa9\xdd:\xac7\xb24\x86E\xee\
-\xb2\x92U\xacV\x8dK\x1c\xe2\xda\x83y>\x98\xe7:\
-;\x7ff\xb9\xa8*Q7\xc6\xb3n\xee\x8f\x8ce\xb1\
-\xe8\xad\xf5j\xb6\xbe1\x22i\x9a\xba\xd8s=\x0f\x81\
-\x05j\xf6\xb5\xa2;4v\x855^r\xf50\xc6.\
-\xe8\x06\xcb\x97Ye\x0d4t\x0d?\xbdy'p\x1a\
-\xb1\x919[\x80\x1fsj\xa6\xdc\xcf\x8f\x9f{%\xc2\
-N\xa1\x8a\xa30]?GYGM\xaei\xc5\x9a5\
-\xcdY\xe3vr\xe3\xbf\xe5\x85ZMm/p|\xec\
-\x93\xd4\xab*#^1\xbe\x5c\xa9s\xf93g\xdb\x9f\
-\xc5njc\x0b[\xbdr\xf8v0\x1aPC\x8c\x80\
-\x17S\x1b\xeaN\x0e\x0fm\xfa\xac7\xc3N\xea9\x81\
-\xf5>,h\x94\xa4\xb9O\xfc\x8f\x96\x87I\x8a0A\
-$\xf8`\xbc\xba\xd2\xb3B\xe4\xba\x16\x00\x1c\xdb\xed\x1e\
-\x9d\xae\x9d}T\xb6[\x0b\xa9\xd0\x82\x97\xec`\xe7\xae\
-D\xc5\xdc-g\x05\xabh\xed\x16\xec\x99\x95b\xadQ\
-\xe4\x9a\x10\xeeV\xc8'\xd3\x9a\xc33\x8a\x02g]_\
-\x8e\xba+\xd60\xaa(r\x92\x04\xc0\x92^\xb4\xd9\x9f\
-\xd8\xcc\xc0hR\xb0E\xa3\x8d\x0f\xbd\xd0O\x9em\xb9\
-F\xd5W\xa6\x97[\xe8\x06\x0f\x86s\xda\x1c\xc6dY\
-k\xba\x04H\x97BN\xed\xfb\x85\xf9\xb4\x8a\xb9\x90\x05\
-\x93\x9d*2\x9f\x91J@m\x5c\xed\x0f\x9b\xb8\x8d\xdd\
-\xadYG\xed\xf5\xf8\xb2\xbeY\xd1Bl\x01\x0b\xa7\xca\
-oBT\xda+\x08\xbd0H\xfc\xe0T\x9f\x03N\x10\
-(\x1c\x92\xe0(>\xd3BF/\x0e\x9c\x14\xfbqx\
-\xaa\x84)o\xf4\x88\xd0\xa6\xe6\x0a6S\x0b\xbfc\xf7\
-\x8d\x94\xda\xa0\xa4{\x06\x85\x9b_\xa45jVb\xbb\
-\x94\xba\x81\x0bZ\xf6\x1d\xec]\xb7\xbc\x86\x82P\x0b}\
-@\xedY\xd9\xadE\xb7\x0b\xd2\xf4l\x81\xad\x85\xde\x09\
-WT\xfb\xeb\xaa\x8a\xeex\xc5\xbf1X 1\xf8\x00\
-\x18\xf46z\xe1\x077\xcbR{\xbd\xa7w{-\xb3\
-;\xa1\xaeK\x0b\x820\xf4z\xa1\x90|\xc9k\xd3p\
-\xcf\xc1\xb1\x87\x03r\xa2\x83\xf5 \x12\xc5\x8e\x1f\xf9Q\
-\xda\x81\xcf=G\x9f\x91WL\xd1\x82*:@\xb1\x93\
-\x84\xdd\x82\x81#\xb3\xdf?\x7f\x99\xb5Y&y\x9e\xfd\
-\x05;\xa9KjY\xda\x80\xce\xc5\x06\x1ah\xcfz\xf1\
-\xa4\xc83`\xb5\x8a\xaa\x19\xaf\x00]\x9a\x10\x7f\x04\x16\
-\x9b\xb8\x83bd\xac{0\x04=\x84\x95\xec@\x8f\x17\
-\xdf\x11E^q\xed\xe4\xfe\xa1xY~\xd5I\xdar\
-\x8f\x82rU\xb2\x99\xc9y\xf8\xdaU\xe1\xb6e\xb4E\
-\xbaGUN\xdc\xae\x07\xe6iy2\xdb\x92\xceY9\
-\xb5\x7f\xd5@\xb4\xc8\xe9\xe4\x97Rl\xd6\x95(X\x0b\
-U{\xe8\xec\x08\xbaJ\xd2\xba\xd1m\x98\xda\xe6k\x09\
-o\xd2\xf7\xc3L?\x22\x82C\xc7\xf7}\xe2}\xe8\x06\
-\x91s\x99\x97}\x8b\x1a\xb5/!\x09\xf0_\x99\xdd\xe7\
-\xc9\x9c\xcd\xe3\x87FI\xf1\xc4\xb2{l>\xed\xe3\x01\
-\xff\x19v\x12\xcf\xd7b\xd2\xc9\xa1{L\x96\x00P\x95\
-\x05\x9d\xac\xa0\xc0\x02R\xd2}V\xc3\xdb\xbe\x93\xb6\xcc\
-\x91\x91\x116\xd7T\xad\x02 \x84^\xa8y \x0c\x1c\
-\xcf\x03\xe8\xc5\x83T3\x12\x89\x9d \x09\xe2\x01\xc7R\
-\x0b\x9d8\xc08\x8d\xfa\xa9M\x14\xdb\xa9\xce\x02\xb0\x92\
-\x19\xba\x86L\x80\x03&\x9f\x99}Z\xbc\x00b0\xdf\
-a\xb9\x80\xa8\xf2\xc1H\x9e\xa9\xe4\xb4V#\xd9\xd6l\
-\xf1\x91\x08\x8ac*_\x8de\xb0[3\x82\x9d0\x81\
-\xde\xfb\xd8[\xef\x1eJ^\xb3\x96!2\xe2x\xe1\xc1\
-pA+^\xee\xb3w?A\xaa\xd2\xfa\xb9\xa4\xf9\xd3\
-\xbb\x07\xd4\xc1\x00\x1d\x82\xadY\xce\x17<\x87c\x8a\xa8\
-G\xa6\x1f\xad\xdfL\xd2w\xa3\x05\xa3\x92/\xa9\xda@\
-\xb1\x97\xcaA\x10\xf8\xb2\xa2\x06\x0a\x95<\x1f\xe9\x16\xcc\
-DB\x0dS\x0a\x8er\xbd\xa3\xee0\xa2\x90\xa8\xce\xe0\
-T'\xd5C\x09\x06L\x22\xddi\xb0\xcb0T\x0c/\
-\xcab,\x90\x5c\x07A\x1a\xd7Y)\x91\x9a\xb7q\xea\
-|%d\x1b\xe8\x80DJ\x03\x98\xa9y\x18`\xd3\x01\
-\xf3\x18S\x1d*\xbd(\xd0\xcd\x1e\x90\xa1\xb9\x8dxp\
-\xa4 q\x90\xf6R\x00Q\x88\x1d\x12c\x12\xc4#\x14\
-\xea\x85\xc0\x8b)\xe8\x85G;K\x0ae\xb6U\x8aa\
-\x13M\x14\x94T\x0f\x0c\xd33\xa2\x14\x1aKz\xcc\xf6\
-\xa05\xa1\xb5\x03\xc4\xf6\x8e\xe4\x97Wwe}\xff-\
-P\xff_\xb8\xfc\xf7\x98\xbb\x06\xacY=q\xcd(\xe1\
-=\xa0C\xdc\xb8\xe6\xc65\xc3n\x86]\x1b\xc5q\x1a\
-\xf9\xc7TC\x88\xe7\xa4Q\xe8\x91KT\x93\xbc\x86K\
-\xa21\x97\x9cg\xbf\x92\xffF%o\x82J\xd8\x8dJ\
-\xde\xc6L\xdf\x22\x95\x10\x12\xc0\xf1\xdd\x8f\xc2c*A\
-a\x02'b\x12\x90\xe8\x02\x97\x1c\xf9\xf77\x88\xfe\xfc\
-\x82r\xb8\xf7B\x8d\xe6FO\x12/\x89\xbd\x97\x98\xeb\
-\x13\x0a\x5c#\xd2 H\xc2\xe8\x1fNE\xaf;\x14\xf9\
-xLd\xe7\xb5_\xab\xfe\xc6do\x82\xc9v7&\
-{\x1b3}\x8bL\x86\xc2\x18\xce%$\x8c\xbc\x11\x95\
-\xe9\x8bOB\xfc$\xbcDe\xd1\x0b\xb9\x092\xfa$\
-\x0cB\x1c\xbe\x8c\xfb\x10\xcc\x1cG\x01N\xc9%2\x03\
-\xbf\x12nx\xe4U\x5c\x16\x9c\x5c\xf0\xce\xab\xbfV\xff\
-\x8d\xccndv#\xb3\xefFf\xed\x1f2_\x7f\xc3\
-\xf3\x03\xdfOF7<\xd8\xca\xbew\xfc\xb7\x9d\x81\xca\
-\x02\xfc\x1a2I\xceox\xe3\xecW\xf2\xdf\xa8\xe4;\
-S\xc9U\xf6x<g\x8f\x89\xbb\x9c\xddM\xf4\xff,\
-fw\x7f\x038y\x18\x8e\
+\x00\x1f#x\xda\xedY\xdb\x8e\xdb6\x10}\xdf\xafP\
+\xb5\x0fI\xd0P\x22u\x97\xd6v\xd06\x08\x10\xa0\xe8\
+C\xbbE\x9f\xb9\x12m\x13\x91D\x83\xa2\xd7v\xbe\xbe\
+C\xeaf\xf9Rl\xb1H\x11\x146\xb0Xk\xe6p\
+\x863<<\x22wg\x1f\xf6Ui=3\xd9pQ\
+\xcfm\xe2`\xdbbu.\x0a^\xaf\xe6\xf6\x9f\x8f\x9f\
+Pb[\x8d\xa2uAKQ\xb3\xb9]\x0b\xfb\xc3\xe2\
+n\xf6\x03B\xd6/\x92Q\xc5\x0ak\xc7\xd5\xda\xfa\x5c\
+\x7fir\xbaa\xd6\xdb\xb5R\x9b\xccuw\xbb\x9d\xc3\
+;\xa3#\xe4\xca}g!\xb4\xb8\xbb\x9b5\xcf\xab;\
+\xcb\xb2 o\xdddE>\xb7\xbb\x01\x9b\xad,\x0d\xb0\
+\xc8]V\xb2\x8a\xd5\xaaq\x89C\x5c{\x84\xe7#<\
+\xd7\xd9\xf93\xcbEU\x89\xba1#\xeb\xe6\xfe\x08,\
+\x8b\xe5\x80\xd6\xb3\xd9\xf9\x06D\xd24u\xb1\xe7z\x1e\
+\x02\x04j\x0e\xb5\xa2{4\x1d\x0as\xbc4\xd4\xc3\x18\
+\xbb\xe0\x1b\x91/Ce\x0d4t\x03?\x03\xbc78\
+\x8d\xd8\xca\x9c-a\x1csj\xa6\xdc\x8f\x8f\x1f\x07'\
+\xc2N\xa1\x8a\xa30}?'Y'M\xaei\xc5\x9a\
+\x0d\xcdY\xe3\xf6v3~\xc7\x0b\xb5\x9e\xdb^\xe0\xf8\
+\xd8'\xa9WU\xc6\xbcf|\xb5V\xe7\xf6g\xcev\
+?\x8b\xfd\xdc\xc6\x16\xb6\x06\xe7\xf8\xad\x05\x8d\xac!\xc6\
+\xc0\x8b\xb9\x0du'\xedC\x97>\x1b`\xd8I='\
+\xb0\xde\x86\x05\x8d\x924\xf7\x89\xff\xde\xf20I\x11&\
+\x88\x04\xef\xcc\xa8\xbe\xf4\xac\x10\xb9\xae\x05\x08\xc7\xf6\xfb\
+G\xa7o\xe7\x10\x95\xed7B*\xb4\xe4%kq\xee\
+ZT\xcc\xddqV\xb0\x8a\xd6n\xc1\x9eY)6\x9a\
+E\xae\x09\xe1\xee\x84\xfcbZ\xd3>\xa3(p6\xf5\
+\xe5\xa8\xfbb\x03K\x15EN\x92\x00Y\xd2\x8b\x98\xc3\
+\x09f\x01\xa0Y\xc1\x96\x8d\x06\xb7\xbd\xd0O\x9em\xb9\
+\xc65T\xa6\xa7[\xe8\x06\x8f\xc0'\xda\xb4\xcbdY\
+\x1b\xba\x02J\x97B\xce\xed\xfb\xa5\xf9t\x8e'!\x0b\
+&{Wd>\x13\x97\x80\xda\xb8:\xb4\x9b\xb8\x8b\xdd\
+\xcfYG\x1d\xfc\xf8\xb2\xbfY\xd3B\xec\x80\x0b\xa7\xce\
+\xafBTzT\x10za\x90\xf8\xc1\xa9?\x07\x9e \
+p8$\xc1Q|\xe6\x85\x8c^\x1c8)\xf6\xe3\xf0\
+\xd4\x09\xab\xbc\xd5K\x84\xb65W\xb0\x99:\xfa\x1d\x0f\
+\xdfJ\xa9\x01%=0(\xdc\xfc\x22\x1d\xa8Y\x8b\xdd\
+J\xea\x06.i9tp\x18\xba\xe35\x14\x84:\xea\
+\x03k\xf1\x15D\xbf\x0b\xd24\xbc\x82\xd0;\xe1\x8a\xeb\
+p\xddU\xd1=\xaf\xf8W\x06\x13$\x86\x1f@\x83\x01\
+\xa3'\xde\x0e\xb3,u\xd0{z\x7f\xd06\xbb7\xea\
+\xba\xb4!\x08Co0\x0a\xc9W\xbc6\x0d\xf7\x1c\x1c\
+{8 '>\x98\x0f\x22Q\xec\xf8\x91\x1f\xa5=\xf9\
+\xdcs\xf6\x19{\xc5\x14-\xa8\xa2#\x15{K\xd8O\
+\x1842\xfb\xfd\xe3\xa7E\x97e\x96\xe7\xd9_\xb0\x93\
+\xfa\xa4\x96\xa5\x01\xf4Il\xa1\x81\xf6b0\xcf\x8a<\
+\x03U\xab\xa8Z\xf0\x0a\xd8\xa5\x05\xf1GP\xb1\x99;\
+:&`\xdd\x831h\x1bV\xb2V\x1e/\xbe#\x8a\
+\xbc\xe2z\x90\xfb\x87\xe2e\xf9Y'\xe9\xca=\x0a\xca\
+U\xc9\x16&g\xfb\xb5\xaf\xc2\xed\xca\xe8\x8at\x8f\xaa\
+\x9c\xb9}\x0f\xcc\xd3\xeadmK\xfa\xc4\xca\xb9\xfd\xab\
+&\xa2ENW~%\xc5vS\x89\x82uT\xb5\xc7\
+\xceN\xa8\xab$\xad\x1b\xdd\x86\xb9m\xbe\x96\xf0&}\
+;\xae\xe9{Dp\xe8\xf8\xbeO\xbcw\xfdB\xe4\x5c\
+\xe6\xe5\xd0\xa2F\x1dJH\x02\xfaWf\xf7y\xf2\xc4\
+\x9e\xe2\x87FI\xf1\x85e\xf7\xd8|\xba\xc7\x96\xff\x19\
+v\x12\xcf\xd7f\xd2\xdb\xa1{L\x96@P\x95\x05\xbd\
+\xad\xa0\xa0\x02R\xd2CV\xc3\xdb\xbe\xb7v\xca\x91\x91\
+\x0977T\xad\x03\x10\x84\xc1\xa8u \x0c\x1c\xcf\x03\
+\xea\xc5\xa3U+\x12\x89\x9d \x09\xe2\x91\xc7R\x1b\x9d\
+8\xc08\x8d\x86U\x9b)\xb6W=\x02\xb8\x92\x19\xb9\
+\x86L\xc0\x03&\x9f\x99}Z\xbc\x00a0\xdfa\xba\
+\xc0\xa8\xf2\xc1X\x9e\xa9\xe4\xb4V\x13\xdb\xcel\xf1\x89\
+\x09\x8ac*_Om\xb0[3\x82\x9d0\x81\xde\xfb\
+\xd8\xdb\xec\x1fJ^\xb3N!2\xe2xa\x0b\x5c\xd2\
+\x8a\x97\x87\xec\xcdO\x90\xaa\xb4~.i\xfe\xe5\xcd\x03\
+\xeai\x80\xda`\x1b\x96\xf3%\xcf\xe1\x98\x22\xea\x09\xf4\
+\xbd\xf5\x9bI\xfaf2aT\xf2\x15U[(\xf6R\
+9\x08\x02_v\xd4 \xa1\x92\xe7\x13\xdf\x92\x99H\xa8\
+aJ\xc1Qn\x18\xa8;\x8c($\xaa38\xd5I\
+\xf5P\x02\x80I\xa4;\x0d\xb8\x0cC\xc5\xf0\xa2,\xa6\
+\x06\xc9u\x10\xa4y\x9d\x95\x12\xa9\xa7.N\x9d\xaf\x85\
+\xec\x02\xb5L\xa44\x8055\x0f#mzb\x1es\
+\xaag\xa5\x17\x05\xba\xd9#3\xb4\xb6\x11\x0f\x8e\x14$\
+\x0e\xd2\xc1\x0a$\x0a\xb1CbL\x82x\xc2B=\x11\
+x1\x05\x83\xf1hgI\xa1\xcc\xb6J1l\xa2\x99\
+\x82\x92\xeaQa\x06E\x94BsI/\xb3=zM\
+h=\x00b{G\xf6\xcb\xb3\xbb2\xbf\xff\x96\xa8\xff\
+/^\xfe{\xce]#\xd6\xa2\x9e\xb9f)\xe1=\xa0\
+C\xdc\xb4\xe6\xa65\xe3n\x86]\x1b\xc5q\x1a\xf9\xc7\
+RC\x88\xe7\xa4Q\xe8\x91KR\x93\xbcFK\xa2\xa9\
+\x96\x9cg\xbf\x92\xff&%\xdf\x85\x94\xb0\x9b\x94\xdc\xa4\
+\xe4\x9a\x94\x10\x12\xc0\xf1\xdd\x8f\xc2c)Aa\x02'\
+b\x12\x90\xe8\x82\x96\x1c\x8d\x1fn\x10\xc3\xf9\x05\xe5p\
+\xef\x85\x1a\xcd\x8d\x9e$^\x12{/\x81\xeb\x13\x0a\x5c\
+#\xd2 H\xc2\xe8\x1fNE\xaf;\x14\xf9x*d\
+\xe7\xb5_\xab\xfe\xa6d\xdf\x85\x92\xedoJvS\xb2\
+\xab\x17\xb00\x86s\x09\x09#o\x22e\xfa\xe2\x93\x10\
+?\x09/IY\xf4Bm\x82\x8c>\x09\x83\x10\x87/\
+\xd3>\x04k\x8e\xa3\x00\xa7\xe4\x92\x98\xc1\xb8\x12nx\
+\xe4UZ\x16\x9c\x5c\xf0\xce\xab\xbfV\xffM\xccnb\
+v\x13\xb3o&f\xdd\x1f2_\x7f\xc3\xf3\x03\xdfO\
+&7<\xd8\xca\xbew\xfc\xb7\x9dQ\xca\x02\xfc\x1a1\
+I\xceox\xd3\xecW\xf2\xdf\xa4\xe4\x1bK\xc9U\xf5\
+x<W\x8f\x99\xbbZ\xdc\xcd\xf4\xff,\x16w\x7f\x03\
+8y\x18\x8e\
 "
 
 qt_resource_name = b"\
@@ -264,9 +264,9 @@
 \x00\x00\x00\x00\x00\x02\x00\x00\x00\x02\x00\x00\x00\x02\
 \x00\x00\x00\x00\x00\x00\x00\x00\
 \x00\x00\x00\x10\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\
-\x00\x00\x01{M\xfa\x09 \
+\x00\x00\x01\x7f\x11a`\x7f\
 \x00\x00\x006\x00\x01\x00\x00\x00\x01\x00\x00\x08h\
-\x00\x00\x01{M\xfa\x09!\
+\x00\x00\x01\x7f\x11a`\x81\
 "
 
 def qInitResources():
diff --git a/nexxT/examples/__init__.py b/nexxT/examples/__init__.py
index 6f08e94..bda3c23 100644
--- a/nexxT/examples/__init__.py
+++ b/nexxT/examples/__init__.py
@@ -12,7 +12,7 @@
 import os
 from nexxT.interface import FilterSurrogate
 if os.environ.get("READTHEDOCS", None) is None:
-    from PySide2 import QtMultimedia # needed to load corresponding DLL before loading the nexxT plugin
+    from nexxT.Qt import QtMultimedia # needed to load corresponding DLL before loading the nexxT plugin
 
 AviReader = FilterSurrogate(
     "binary://" + str((Path(__file__).parent.parent / "tests" /
diff --git a/nexxT/examples/framework/ImageData.py b/nexxT/examples/framework/ImageData.py
index 4071a14..fe708cc 100644
--- a/nexxT/examples/framework/ImageData.py
+++ b/nexxT/examples/framework/ImageData.py
@@ -14,7 +14,7 @@
 
 import ctypes as ct
 import numpy as np
-from PySide2.QtCore import QByteArray
+from nexxT.Qt.QtCore import QByteArray
 
 # The supported image formats and the mapping to the number of channels and the numpy type.
 # Note that QT's QImage natively only supports intensity_u8, intensity_u16 and rgb_u8 images.
@@ -59,7 +59,7 @@ def byteArrayToNumpy(qByteArray):
     fmt = hdr.format.decode()
     # sanity check
     if not fmt in ImageFormats:
-        raise RuntimeError("Unknown image format %s" % fmt)
+        raise RuntimeError(f"Unknown image format {fmt}")
     # get number of channels and the numpy dtype of the target array
     numChannels, dtype = ImageFormats[hdr.format.decode()]
     # calculate the number of bytes per pixel
diff --git a/nexxT/examples/framework/ImageView.py b/nexxT/examples/framework/ImageView.py
index fe41c39..bd78a59 100644
--- a/nexxT/examples/framework/ImageView.py
+++ b/nexxT/examples/framework/ImageView.py
@@ -12,8 +12,8 @@
 
 import logging
 import numpy as np
-from PySide2.QtGui import QPainter, QImage
-from PySide2.QtWidgets import QWidget
+from nexxT.Qt.QtGui import QPainter, QImage
+from nexxT.Qt.QtWidgets import QWidget
 from nexxT.interface import Filter, Services
 from nexxT.examples.framework.ImageData import byteArrayToNumpy
 
diff --git a/nexxT/filters/GenericReader.py b/nexxT/filters/GenericReader.py
index 7c2cf62..e900e5e 100644
--- a/nexxT/filters/GenericReader.py
+++ b/nexxT/filters/GenericReader.py
@@ -11,8 +11,8 @@
 import time
 import logging
 import math
-from PySide2.QtCore import Signal, QTimer, Qt
-from PySide2.QtWidgets import QFileDialog
+from nexxT.Qt.QtCore import Signal, QTimer, Qt
+from nexxT.Qt.QtWidgets import QFileDialog
 from nexxT.interface import Filter, Services, DataSample
 from nexxT.core.Utils import handleException, isMainThread, MethodInvoker
 
@@ -30,7 +30,6 @@ class GenericReaderFile:
     See :py:class:`nexxT.filters.hdf5.Hdf5File` and :py:class:`nexxT.filters.hdf5.Hdf5Reader` for an example.
     """
 
-    # pylint: disable=no-self-use
     # this is an abstract class and the methods are provided for reference
 
     def close(self):
@@ -105,7 +104,7 @@ class GenericReader(Filter):
 
     # methods to be overloaded
 
-    def getNameFilter(self): # pylint: disable=no-self-use
+    def getNameFilter(self):
         """
         Returns the name filter associated with the input files.
 
@@ -113,7 +112,7 @@ def getNameFilter(self): # pylint: disable=no-self-use
         """
         raise NotImplementedError()
 
-    def openFile(self, filename): # pylint: disable=no-self-use
+    def openFile(self, filename):
         """
         Opens the given file and return an instance of GenericReaderFile.
 
@@ -369,7 +368,7 @@ def onSuggestDynamicPorts(self):
         """
         try:
             fn, ok = QFileDialog.getOpenFileName(caption="Choose template hdf5 file",
-                                                 filter="Support files (%s)" % (" ".join(self.getNameFilter())))
+                                                 filter=f"Support files ({' '.join(self.getNameFilter())})")
             if ok:
                 f = self.openFile(fn) # pylint: disable=assignment-from-no-return
                 if not isinstance(f, GenericReaderFile):
@@ -389,6 +388,8 @@ def _timeSpan(self):
             tmin = min(t, tmin)
             t = self._file.getRcvTimestamp(p, self._file.getNumberOfSamples(p)-1)
             tmax = max(t, tmax)
+        if tmin > tmax:
+            raise RuntimeError("It seems that the input file doesn't have any usable samples.")
         return (tmin*(1000000000//self._file.getTimestampResolution()),
                 tmax*(1000000000//self._file.getTimestampResolution()))
 
@@ -453,7 +454,7 @@ def _transmit(self, pname):
         self._ports[[p.name() for p in self._ports].index(pname)].transmit(sample)
         self._currentTimestampChanged(rcvTimestamp*(1000000000//self._file.getTimestampResolution()))
         if self._untilStream is not None:
-            if self._untilStream == pname or self._untilStream == '':
+            if self._untilStream in (pname, ''):
                 self.pausePlayback()
         return res
 
diff --git a/nexxT/filters/hdf5.py b/nexxT/filters/hdf5.py
index fdcafce..e144f0e 100644
--- a/nexxT/filters/hdf5.py
+++ b/nexxT/filters/hdf5.py
@@ -16,7 +16,7 @@
 import os
 import numpy as np
 import h5py
-from PySide2.QtCore import Signal
+from nexxT.Qt.QtCore import Signal
 from nexxT.interface import Filter, Services
 from nexxT.core.Utils import handleException, isMainThread
 from nexxT.filters.GenericReader import GenericReader, GenericReaderFile
diff --git a/nexxT/include/nexxT/Compatibility.hpp b/nexxT/include/nexxT/Compatibility.hpp
new file mode 100644
index 0000000..f2f30d9
--- /dev/null
+++ b/nexxT/include/nexxT/Compatibility.hpp
@@ -0,0 +1,32 @@
+/*
+ * SPDX-License-Identifier: Apache-2.0
+ * Copyright (C) 2020 ifm electronic gmbh
+ *
+ * THE PROGRAM IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND.
+ */
+
+/**
+    \file Compatibility.hpp
+*/
+
+#ifndef NEXXT_COMPATIBILITY_HPP
+#define NEXXT_COMPATIBILITY_HPP
+
+#include <QtGui/QAction>
+#include <QtWidgets/QMenu>
+
+#include "nexxT/NexxTLinkage.hpp"
+
+//! @cond Doxygen_Suppress
+namespace nexxT
+{
+    class DLLEXPORT Compatibility
+    {
+    public:
+        // TODO: remove after https://bugreports.qt.io/browse/PYSIDE-1627 has been fixed
+        static QMenu *getMenuFromAction(QAction *);
+    };
+};
+//! @endcond
+
+#endif
diff --git a/nexxT/src/DataSamples.hpp b/nexxT/include/nexxT/DataSamples.hpp
similarity index 97%
rename from nexxT/src/DataSamples.hpp
rename to nexxT/include/nexxT/DataSamples.hpp
index 2d67986..198be46 100644
--- a/nexxT/src/DataSamples.hpp
+++ b/nexxT/include/nexxT/DataSamples.hpp
@@ -18,8 +18,8 @@
 #include <QtCore/QString>
 #include <QtCore/QSharedPointer>
 
-#include "NexxTLinkage.hpp"
-#include "SharedPointerTypes.hpp"
+#include "nexxT/NexxTLinkage.hpp"
+#include "nexxT/SharedPointerTypes.hpp"
 
 namespace nexxT
 {
diff --git a/nexxT/src/FilterEnvironment.hpp b/nexxT/include/nexxT/FilterEnvironment.hpp
similarity index 95%
rename from nexxT/src/FilterEnvironment.hpp
rename to nexxT/include/nexxT/FilterEnvironment.hpp
index b8cc059..6f729b2 100644
--- a/nexxT/src/FilterEnvironment.hpp
+++ b/nexxT/include/nexxT/FilterEnvironment.hpp
@@ -10,9 +10,9 @@
 
 #include <QtCore/QObject>
 
-#include "SharedPointerTypes.hpp"
-#include "Filters.hpp"
-#include "NexxTLinkage.hpp"
+#include "nexxT/SharedPointerTypes.hpp"
+#include "nexxT/Filters.hpp"
+#include "nexxT/NexxTLinkage.hpp"
 
 namespace nexxT
 {
diff --git a/nexxT/src/Filters.hpp b/nexxT/include/nexxT/Filters.hpp
similarity index 98%
rename from nexxT/src/Filters.hpp
rename to nexxT/include/nexxT/Filters.hpp
index 489f436..8b54bb6 100644
--- a/nexxT/src/Filters.hpp
+++ b/nexxT/include/nexxT/Filters.hpp
@@ -18,8 +18,8 @@
 #include <QtCore/QObject>
 #include <QtCore/QList>
 
-#include "NexxTLinkage.hpp"
-#include "SharedPointerTypes.hpp"
+#include "nexxT/NexxTLinkage.hpp"
+#include "nexxT/SharedPointerTypes.hpp"
 
 namespace nexxT
 {
diff --git a/nexxT/src/InputPortInterface.hpp b/nexxT/include/nexxT/InputPortInterface.hpp
similarity index 94%
rename from nexxT/src/InputPortInterface.hpp
rename to nexxT/include/nexxT/InputPortInterface.hpp
index 6774af4..c382ebd 100644
--- a/nexxT/src/InputPortInterface.hpp
+++ b/nexxT/include/nexxT/InputPortInterface.hpp
@@ -15,9 +15,9 @@
 
 #include <QtCore/QObject>
 #include <QtCore/QSemaphore>
-#include "NexxTLinkage.hpp"
-#include "SharedPointerTypes.hpp"
-#include "Ports.hpp"
+#include "nexxT/NexxTLinkage.hpp"
+#include "nexxT/SharedPointerTypes.hpp"
+#include "nexxT/Ports.hpp"
 
 namespace nexxT
 {
diff --git a/nexxT/src/Logger.hpp b/nexxT/include/nexxT/Logger.hpp
similarity index 97%
rename from nexxT/src/Logger.hpp
rename to nexxT/include/nexxT/Logger.hpp
index 112a531..6ba3ddc 100644
--- a/nexxT/src/Logger.hpp
+++ b/nexxT/include/nexxT/Logger.hpp
@@ -14,8 +14,8 @@
 #ifndef NEXXT_LOGGER_HPP
 #define NEXXT_LOGGER_HPP
 
-#include "Services.hpp"
-#include "NexxTLinkage.hpp"
+#include "nexxT/Services.hpp"
+#include "nexxT/NexxTLinkage.hpp"
 #include <QtCore/QMetaObject>
 
 //! @cond Doxygen_Suppress
diff --git a/nexxT/src/NexxTLinkage.hpp b/nexxT/include/nexxT/NexxTLinkage.hpp
similarity index 100%
rename from nexxT/src/NexxTLinkage.hpp
rename to nexxT/include/nexxT/NexxTLinkage.hpp
diff --git a/nexxT/src/NexxTPlugins.hpp b/nexxT/include/nexxT/NexxTPlugins.hpp
similarity index 95%
rename from nexxT/src/NexxTPlugins.hpp
rename to nexxT/include/nexxT/NexxTPlugins.hpp
index c8b2aac..781f6af 100644
--- a/nexxT/src/NexxTPlugins.hpp
+++ b/nexxT/include/nexxT/NexxTPlugins.hpp
@@ -14,13 +14,14 @@
 #ifndef NEXXT_PLUGINS_HPP
 #define NEXXT_PLUGINS_HPP
 
-#include "Filters.hpp"
+#include "nexxT/Filters.hpp"
 /* included for convenience, these headers are usually needed in every filter */
-#include "DataSamples.hpp"
-#include "SharedPointerTypes.hpp"
-#include "OutputPortInterface.hpp"
-#include "InputPortInterface.hpp"
+#include "nexxT/DataSamples.hpp"
+#include "nexxT/SharedPointerTypes.hpp"
+#include "nexxT/OutputPortInterface.hpp"
+#include "nexxT/InputPortInterface.hpp"
 #include <QtCore/QLibrary>
+#include <QtCore/QMap>
 
 /*!
     This macro must be present in the Filter's class declaration.
diff --git a/nexxT/src/OutputPortInterface.hpp b/nexxT/include/nexxT/OutputPortInterface.hpp
similarity index 90%
rename from nexxT/src/OutputPortInterface.hpp
rename to nexxT/include/nexxT/OutputPortInterface.hpp
index 4334d2b..1371f95 100644
--- a/nexxT/src/OutputPortInterface.hpp
+++ b/nexxT/include/nexxT/OutputPortInterface.hpp
@@ -15,9 +15,9 @@
 
 #include <QtCore/QObject>
 #include <QtCore/QSemaphore>
-#include "NexxTLinkage.hpp"
-#include "SharedPointerTypes.hpp"
-#include "Ports.hpp"
+#include "nexxT/NexxTLinkage.hpp"
+#include "nexxT/SharedPointerTypes.hpp"
+#include "nexxT/Ports.hpp"
 
 namespace nexxT
 {
@@ -69,7 +69,7 @@ namespace nexxT
         /*!
             Called by the nexxT framework, not intended to be used directly.
         */
-        static QObject *setupInterThreadConnection(const SharedPortPtr &, const SharedPortPtr &, QThread &);
+        static QObject *setupInterThreadConnection(const SharedPortPtr &, const SharedPortPtr &, QThread &, int width);
     };
 
 };
diff --git a/nexxT/src/Ports.hpp b/nexxT/include/nexxT/Ports.hpp
similarity index 95%
rename from nexxT/src/Ports.hpp
rename to nexxT/include/nexxT/Ports.hpp
index 49c5068..9d26bc9 100644
--- a/nexxT/src/Ports.hpp
+++ b/nexxT/include/nexxT/Ports.hpp
@@ -15,8 +15,8 @@
 
 #include <QtCore/QObject>
 #include <QtCore/QSemaphore>
-#include "NexxTLinkage.hpp"
-#include "SharedPointerTypes.hpp"
+#include "nexxT/NexxTLinkage.hpp"
+#include "nexxT/SharedPointerTypes.hpp"
 
 namespace nexxT
 {
@@ -89,7 +89,7 @@ namespace nexxT
 
         InterThreadConnectionD *const d;
     public:
-        InterThreadConnection(QThread *qthread_from);
+        InterThreadConnection(QThread *qthread_from, int width);
         virtual ~InterThreadConnection();
 
     signals:
diff --git a/nexxT/src/PropertyCollection.hpp b/nexxT/include/nexxT/PropertyCollection.hpp
similarity index 99%
rename from nexxT/src/PropertyCollection.hpp
rename to nexxT/include/nexxT/PropertyCollection.hpp
index 7d2e9d4..0c59c2e 100644
--- a/nexxT/src/PropertyCollection.hpp
+++ b/nexxT/include/nexxT/PropertyCollection.hpp
@@ -15,7 +15,7 @@
 
 #include <QtCore/QObject>
 #include <QtCore/QVariant>
-#include "NexxTLinkage.hpp"
+#include "nexxT/NexxTLinkage.hpp"
 
 namespace nexxT
 {
diff --git a/nexxT/src/Services.hpp b/nexxT/include/nexxT/Services.hpp
similarity index 98%
rename from nexxT/src/Services.hpp
rename to nexxT/include/nexxT/Services.hpp
index 0f137a3..bc14e0f 100644
--- a/nexxT/src/Services.hpp
+++ b/nexxT/include/nexxT/Services.hpp
@@ -16,7 +16,7 @@
 #include <QtCore/QObject>
 #include <QtCore/QSharedPointer>
 
-#include "NexxTLinkage.hpp"
+#include "nexxT/NexxTLinkage.hpp"
 
 namespace nexxT
 {
diff --git a/nexxT/src/SharedPointerTypes.hpp b/nexxT/include/nexxT/SharedPointerTypes.hpp
similarity index 100%
rename from nexxT/src/SharedPointerTypes.hpp
rename to nexxT/include/nexxT/SharedPointerTypes.hpp
diff --git a/nexxT/interface/DataSamples.py b/nexxT/interface/DataSamples.py
index d6a0775..370969b 100644
--- a/nexxT/interface/DataSamples.py
+++ b/nexxT/interface/DataSamples.py
@@ -8,7 +8,7 @@
 This module defines the nexxT interface class DataSample.
 """
 import time
-from PySide2.QtCore import QByteArray
+from nexxT.Qt.QtCore import QByteArray
 
 class DataSample:
     """
diff --git a/nexxT/interface/Filters.py b/nexxT/interface/Filters.py
index 80de626..6a79e6a 100644
--- a/nexxT/interface/Filters.py
+++ b/nexxT/interface/Filters.py
@@ -8,7 +8,7 @@
 This module defines the FilterState and Filter classes of the nexxT interface.
 """
 
-from PySide2.QtCore import QObject
+from nexxT.Qt.QtCore import QObject
 from nexxT.interface import OutputPort, InputPort
 
 class FilterState:
@@ -45,7 +45,7 @@ def state2str(state):
         for k in FilterState.__dict__:
             if k[0] != "_" and k.upper() == k and getattr(FilterState, k) == state:
                 return k
-        raise RuntimeError("Unknown state %s" % state)
+        raise RuntimeError(f"Unknown state {state}")
 
 
 class Filter(QObject):
@@ -243,7 +243,7 @@ def environment(self):
         """
         return self._environment
 
-    def onSuggestDynamicPorts(self): # pylint: disable=no-self-use
+    def onSuggestDynamicPorts(self):
         """
         Shall return the suggested dynamic ports of this filter. Prominent example is to return the streams
         contained in a HDF5 file. Note that it is safe to assume that the instance lives in the GUI thread,
diff --git a/nexxT/interface/Ports.py b/nexxT/interface/Ports.py
index bd7f2d3..4313248 100644
--- a/nexxT/interface/Ports.py
+++ b/nexxT/interface/Ports.py
@@ -8,7 +8,7 @@
 This module defines the Port, InputPort and OutputPort interface classes of the nexxT framework.
 """
 
-from PySide2.QtCore import QObject, Signal
+from nexxT.Qt.QtCore import QObject, Signal
 
 class Port(QObject):
     """
diff --git a/nexxT/interface/PropertyCollections.py b/nexxT/interface/PropertyCollections.py
index 30b7792..8c5efdd 100644
--- a/nexxT/interface/PropertyCollections.py
+++ b/nexxT/interface/PropertyCollections.py
@@ -8,7 +8,7 @@
 This module defines the PropertyCollection interface class of the nexxT framework.
 """
 
-from PySide2.QtCore import QObject, Signal, Slot
+from nexxT.Qt.QtCore import QObject, Signal, Slot
 
 class PropertyHandler:
     """
diff --git a/nexxT/interface/__init__.py b/nexxT/interface/__init__.py
index 6f3a06f..2eae388 100644
--- a/nexxT/interface/__init__.py
+++ b/nexxT/interface/__init__.py
@@ -10,13 +10,20 @@
 
 import nexxT
 if nexxT.useCImpl:
-    import cnexxT
+    import cnexxT # pylint: disable=import-error
     # constants here are really classes
     # pylint: disable=invalid-name
     cnexxT = cnexxT.nexxT
     Filter = cnexxT.Filter
     FilterState = cnexxT.FilterState
-    DataSample = lambda *args, **kw: cnexxT.DataSample.make_shared(cnexxT.DataSample(*args, **kw))
+
+    def DataSample(*args, **kw):
+        """
+        DataSample factory function (create a shared pointer to a new DataSample instance)
+        """
+        return cnexxT.DataSample.make_shared(cnexxT.DataSample(*args, **kw))
+    #DataSample = lambda *args, **kw: cnexxT.DataSample.make_shared(cnexxT.DataSample(*args, **kw))
+
     DataSample.TIMESTAMP_RES = cnexxT.DataSample.TIMESTAMP_RES
     DataSample.copy = cnexxT.DataSample.copy
     DataSample.currentTime = cnexxT.DataSample.currentTime
@@ -28,9 +35,22 @@
     PropertyCollection = cnexxT.PropertyCollection
     PropertyHandler = cnexxT.PropertyHandler
     Services = cnexxT.Services
-    OutputPort = lambda *args, **kw: Port.make_shared(OutputPortInterface(*args, **kw))
-    InputPort = (lambda dynamic, name, environment, queueSizeSamples=1, queueSizeSeconds=-1:
-                 Port.make_shared(InputPortInterface(dynamic, name, environment, queueSizeSamples, queueSizeSeconds)))
+
+    def OutputPort(*args, **kw):
+        """
+        OutputPort factory function (create a shared pointer to a new OutputPortInterface instance)
+        """
+        return Port.make_shared(OutputPortInterface(*args, **kw))
+    #OutputPort = lambda *args, **kw: Port.make_shared(OutputPortInterface(*args, **kw))
+
+    def InputPort(dynamic, name, environment, queueSizeSamples=1, queueSizeSeconds=-1):
+        """
+        InputPort factory function (create a shared pointer to a new InputPortInterface instance)
+        """
+        return Port.make_shared(InputPortInterface(dynamic, name, environment, queueSizeSamples, queueSizeSeconds))
+    #InputPort = (lambda dynamic, name, environment, queueSizeSamples=1, queueSizeSeconds=-1:
+    #        Port.make_shared(InputPortInterface(dynamic, name, environment, queueSizeSamples, queueSizeSeconds)))
+
     from nexxT.interface.Filters import FilterSurrogate
 else:
     # pylint: enable=invalid-name
@@ -49,4 +69,8 @@
     from nexxT.interface.PropertyCollections import PropertyCollection, PropertyHandler
     from nexxT.interface.Services import Services
 
+__all__ = ["Services", "PropertyCollection", "PropertyHandler", "DataSample",
+           "Filter", "FilterState", "FilterSurrogate",
+           "Port", "InputPort", "OutputPort", "OutputPortInterface", "InputPortInterface"]
+
 del nexxT
diff --git a/nexxT/services/ConsoleLogger.py b/nexxT/services/ConsoleLogger.py
index 0ff6fc8..d442acf 100644
--- a/nexxT/services/ConsoleLogger.py
+++ b/nexxT/services/ConsoleLogger.py
@@ -12,7 +12,7 @@
 import logging
 import os.path
 import sys
-from PySide2.QtCore import QObject, Slot, qInstallMessageHandler, QtMsgType
+from nexxT.Qt.QtCore import QObject, Slot, qInstallMessageHandler, QtMsgType
 from nexxT.core.Utils import excepthook
 
 logger = logging.getLogger(__name__)
@@ -27,7 +27,7 @@ def makeRecord(self, name, level, filename, lineno, msg, args, excInfo, func=Non
     """
     if extra is not None:
         filename, lineno = extra
-        name = "c++/%s" % (os.path.split(filename)[1])
+        name = f"c++/{os.path.split(filename)[1]}"
     return logging.LogRecord(name, level, filename, lineno, msg, args, excInfo, func, sinfo)
 # pylint: enable=too-many-arguments
 # pylint: enable=unused-argument
@@ -39,7 +39,7 @@ class ConsoleLogger(QObject):
     Logging service to console (using python logging module). This class is used to log messages in C++.
     """
     @Slot(int, str, str, int)
-    def log(self, level, message, file, line): # pylint: disable=no-self-use
+    def log(self, level, message, file, line):
         """
         Called from c++ to log a message.
 
diff --git a/nexxT/services/SrvConfiguration.py b/nexxT/services/SrvConfiguration.py
index b852fb9..d1af877 100644
--- a/nexxT/services/SrvConfiguration.py
+++ b/nexxT/services/SrvConfiguration.py
@@ -9,9 +9,9 @@
 """
 
 import logging
-from PySide2.QtCore import (QObject, Slot, Qt, QAbstractItemModel, QModelIndex, QMimeData)
-from PySide2.QtGui import QFont, QIcon
-from PySide2.QtWidgets import QStyle, QApplication
+from nexxT.Qt.QtCore import (QObject, Slot, Qt, QAbstractItemModel, QModelIndex, QMimeData)
+from nexxT.Qt.QtGui import QFont, QIcon
+from nexxT.Qt.QtWidgets import QStyle, QApplication
 from nexxT.core.Exceptions import NexTRuntimeError
 from nexxT.core.ConfigFiles import ConfigFileLoader
 from nexxT.core.Configuration import Configuration
@@ -441,6 +441,9 @@ def columnCount(self, parent):
         return 2
 
     def headerData(self, section, orientation, role):
+        """
+        Overwritten from QAbstractItemModel. Provide header names for the columns.
+        """
         if orientation == Qt.Horizontal and role == Qt.DisplayRole:
             return ["Name", "Property"][section]
         return super().headerData(section, orientation, role)
@@ -581,7 +584,7 @@ def setData(self, index, value, role):# pylint: disable=too-many-return-statemen
             return True
         return False
 
-    def headerDate(self, section, orientation, role): # pylint: disable=no-self-use
+    def headerDate(self, section, orientation, role):
         """
         Returns the header data of this model
 
@@ -597,10 +600,17 @@ def headerDate(self, section, orientation, role): # pylint: disable=no-self-use
         return None
 
     def mimeTypes(self):
+        """
+        Overwritten from QAbstractItemModel, provide a mime type for copy/pasting
+        """
         logger.debug("mimeTypes")
         return ["application/x-nexxT-compositefilter"]
 
     def mimeData(self, indices):
+        """
+        Overwritten from QAbstractItemModel, provide the mime data for copy/pasting (note that this doesn't work across
+        processes.
+        """
         logger.debug("mimeData")
         if len(indices) == 1 and indices[0].isValid():
             index = indices[0]
@@ -628,7 +638,7 @@ def __init__(self, configuration):
         configuration.appActivated.connect(self.appActivated)
 
     @Slot()
-    def activate(self): # pylint: disable=no-self-use
+    def activate(self):
         """
         Call this slot to activate the current application
 
@@ -647,7 +657,7 @@ def execute():
         execute()
 
     @Slot()
-    def deactivate(self): # pylint: disable=no-self-use
+    def deactivate(self):
         """
         Call this slot to deactivate the current application
 
diff --git a/nexxT/services/SrvPlaybackControl.py b/nexxT/services/SrvPlaybackControl.py
index ef2b2f4..1f705ce 100644
--- a/nexxT/services/SrvPlaybackControl.py
+++ b/nexxT/services/SrvPlaybackControl.py
@@ -10,7 +10,7 @@
 
 import pathlib
 import logging
-from PySide2.QtCore import QObject, Signal, Slot, Qt, QDir, QMutex, QMutexLocker
+from nexxT.Qt.QtCore import QObject, Signal, Slot, Qt, QDir, QMutex, QMutexLocker
 from nexxT.interface import FilterState
 from nexxT.core.Exceptions import NexTRuntimeError
 from nexxT.core.Application import Application
@@ -232,8 +232,8 @@ def setupConnections(self, playbackDevice, nameFilters):
         :return:
         """
         with QMutexLocker(self._mutex):
-            for devid in self._registeredDevices:
-                if self._registeredDevices[devid]["object"] is playbackDevice:
+            for _, dev in self._registeredDevices.items():
+                if dev["object"] is playbackDevice:
                     raise NexTRuntimeError("Trying to register a playbackDevice object twice.")
 
             proxy = PlaybackDeviceProxy(self, playbackDevice, nameFilters)
@@ -271,8 +271,8 @@ def removeConnections(self, playbackDevice):
         """
         with QMutexLocker(self._mutex):
             found = []
-            for devid in self._registeredDevices:
-                if self._registeredDevices[devid]["object"] is playbackDevice:
+            for devid, dev in self._registeredDevices.items():
+                if dev["object"] is playbackDevice:
                     found.append(devid)
             if len(found) > 0:
                 for devid in found:
@@ -317,9 +317,9 @@ def _updateFeatureSet(self):
         assertMainThread()
         featureset = set()
         nameFilters = set()
-        for devid in self._registeredDevices:
-            featureset = featureset.union(self._registeredDevices[devid]["featureset"])
-            nameFilters = nameFilters.union(self._registeredDevices[devid]["nameFilters"])
+        for _, dev in self._registeredDevices.items():
+            featureset = featureset.union(dev["featureset"])
+            nameFilters = nameFilters.union(dev["nameFilters"])
         self._supportedFeaturesChanged(featureset, nameFilters)
 
     def _supportedFeaturesChanged(self, featureset, nameFilters):
diff --git a/nexxT/services/SrvProfiling.py b/nexxT/services/SrvProfiling.py
index 4be5bb6..c9ef95c 100644
--- a/nexxT/services/SrvProfiling.py
+++ b/nexxT/services/SrvProfiling.py
@@ -12,7 +12,7 @@
 import time
 from threading import Lock
 import numpy as np
-from PySide2.QtCore import QObject, Signal, Slot, QThread, QTimer, Qt, QByteArray, QCoreApplication
+from nexxT.Qt.QtCore import QObject, Signal, Slot, QThread, QTimer, Qt, QByteArray, QCoreApplication
 from nexxT.core.Utils import MethodInvoker
 
 logger = logging.getLogger(__name__)
@@ -309,14 +309,14 @@ def _emitData(self):
         if t - self._lastEmitTime > 1e8:
             # emit each 100 ms
             self._lastEmitTime = t
-            for t in self._threadSpecificProfiling:
-                load = self._threadSpecificProfiling[t].getLoad()
+            for t, tsp in self._threadSpecificProfiling.items():
+                load = tsp.getLoad()
                 atimstamps = np.array([l[0] for l in load], dtype=np.int64)
                 aload = np.array([l[1] for l in load], dtype=np.float32)
                 if aload.size > 0:
                     self.loadDataUpdated.emit(t.objectName(), QByteArray(atimstamps.tobytes()),
                                               QByteArray(aload.tobytes()))
-                port_spans = self._threadSpecificProfiling[t].getSpans()
+                port_spans = tsp.getSpans()
                 for port, spans in port_spans.items():
                     spans = np.array(spans, dtype=np.int64)
                     if spans.size > 0:
diff --git a/nexxT/services/SrvRecordingControl.py b/nexxT/services/SrvRecordingControl.py
index edd46e5..430c3a9 100644
--- a/nexxT/services/SrvRecordingControl.py
+++ b/nexxT/services/SrvRecordingControl.py
@@ -9,7 +9,7 @@
 """
 
 import logging
-from PySide2.QtCore import QObject, Signal, Slot, Qt, QMutex, QMutexLocker
+from nexxT.Qt.QtCore import QObject, Signal, Slot, Qt, QMutex, QMutexLocker
 from nexxT.core.Application import Application
 from nexxT.core.Exceptions import NexTRuntimeError
 from nexxT.interface import FilterState
@@ -54,8 +54,8 @@ def setupConnections(self, recordingDevice):
         :return:
         """
         with QMutexLocker(self._mutex):
-            for devid in self._registeredDevices:
-                if self._registeredDevices[devid]["object"] is recordingDevice:
+            for _, dev in self._registeredDevices.items():
+                if dev["object"] is recordingDevice:
                     raise NexTRuntimeError("Trying to register a playbackDevice object twice.")
 
             if not self._startRecording.connect(recordingDevice.startRecording):
@@ -94,8 +94,8 @@ def removeConnections(self, recordingDevice):
         """
         with QMutexLocker(self._mutex):
             found = []
-            for devid in self._registeredDevices:
-                if self._registeredDevices[devid]["object"] is recordingDevice:
+            for devid, dev in self._registeredDevices.items():
+                if dev["object"] is recordingDevice:
                     found.append(devid)
             if len(found) > 0:
                 for devid in found:
@@ -109,12 +109,12 @@ def removeConnections(self, recordingDevice):
     def _updateFeatureSet(self):
         featureset = set()
         featureCount = {}
-        for devid in self._registeredDevices:
-            for f in self._registeredDevices[devid]["featureset"]:
+        for _, dev in self._registeredDevices.items():
+            for f in dev["featureset"]:
                 if not f in featureCount:
                     featureCount[f] = 0
                 featureCount[f] += 1
-            featureset = featureset.union(self._registeredDevices[devid]["featureset"])
+            featureset = featureset.union(dev["featureset"])
         self._supportedFeaturesChanged(featureset)
 
     def _supportedFeaturesChanged(self, featureset):
diff --git a/nexxT/services/gui/BrowserWidget.py b/nexxT/services/gui/BrowserWidget.py
index 9df1a7a..b0c7882 100644
--- a/nexxT/services/gui/BrowserWidget.py
+++ b/nexxT/services/gui/BrowserWidget.py
@@ -12,9 +12,9 @@
 import os
 import platform
 import string
-from PySide2.QtCore import (QAbstractTableModel, Qt, Signal, QModelIndex, QDateTime, QFileInfo, QDir, QEvent)
-from PySide2.QtGui import QKeyEvent, QKeySequence
-from PySide2.QtWidgets import (QWidget, QVBoxLayout, QTreeView, QFileIconProvider, QCompleter, QLineEdit, QHeaderView)
+from nexxT.Qt.QtCore import (QAbstractTableModel, Qt, Signal, QModelIndex, QDateTime, QFileInfo, QDir, QEvent)
+from nexxT.Qt.QtGui import QKeyEvent, QKeySequence
+from nexxT.Qt.QtWidgets import (QWidget, QVBoxLayout, QTreeView, QFileIconProvider, QCompleter, QLineEdit, QHeaderView)
 
 logger = logging.getLogger(__name__)
 
@@ -108,8 +108,8 @@ def _reset(self, folder, flt):
             self._filter = flt
             if platform.system() == "Windows":
                 if listDrives:
-                    self._children = [Path("%s:/" % dl) for dl in string.ascii_uppercase
-                                      if self.statCache(Path("%s:/" % dl).exists)]
+                    self._children = [Path(f"{dl}:/") for dl in string.ascii_uppercase
+                                      if self.statCache(Path(f"{dl}:/").exists)]
                 else:
                     self._children = [f / ".."]
             else:
@@ -179,11 +179,11 @@ def data(self, index, role):
                 except Exception: # pylint: disable=broad-except
                     return ""
                 if s >= 1024*1024*1024:
-                    return "%.0f GB" % (s / (1024*1024*1024))
+                    return f"{s / (1024*1024*1024):.0f} GB"
                 if s >= 1024*1024:
-                    return "%.0f MB" % (s / (1024*1024))
+                    return f"{s / (1024*1024):.0f} MB"
                 if s >= 1024:
-                    return "%.0f kB" % (s / 1024)
+                    return f"{s / 1024:.0f} kB"
                 return s
             if index.column() == 2:
                 try:
@@ -270,6 +270,9 @@ def event(self, event):
         return super().event(event)
 
     def keyPressEvent(self, event):
+        """
+        Overwritten from QWidget, intermit paste events to log in the text directly after pasting.
+        """
         ret = super().keyPressEvent(event)
         if event.matches(QKeySequence.Paste):
             self.pasted.emit()
@@ -403,21 +406,3 @@ def _activated(self, idx):
             self.activated.emit(str(c))
         else:
             self._model.setFolder(c)
-
-if __name__ == "__main__": # pragma: no-cover
-    def main():
-        """
-        Test function
-
-        :return:
-        """
-        # this is just the test function part
-        from PySide2.QtWidgets import QApplication # pylint: disable=import-outside-toplevel
-
-        app = QApplication()
-        bw = BrowserWidget()
-        bw.activated.connect(print)
-        bw.setActive("/home/wiedeman/.bashrc")
-        bw.show()
-        app.exec_()
-    main()
diff --git a/nexxT/services/gui/Configuration.py b/nexxT/services/gui/Configuration.py
index 791e270..d4c91fa 100644
--- a/nexxT/services/gui/Configuration.py
+++ b/nexxT/services/gui/Configuration.py
@@ -8,15 +8,16 @@
 This module provides the Configuration GUI service of the nexxT framework.
 """
 import logging
-import shiboken2
-from PySide2.QtCore import (Qt, QSettings, QByteArray, QDataStream, QIODevice, QTimer)
-from PySide2.QtGui import QIcon, QKeySequence
-from PySide2.QtWidgets import (QTreeView, QAction, QStyle, QApplication, QFileDialog, QAbstractItemView, QMessageBox,
+import nexxT.shiboken
+import nexxT.Qt
+from nexxT.Qt.QtCore import (Qt, QSettings, QByteArray, QDataStream, QIODevice, QTimer)
+from nexxT.Qt.QtGui import QIcon, QKeySequence, QAction
+from nexxT.Qt.QtWidgets import (QTreeView, QStyle, QApplication, QFileDialog, QAbstractItemView, QMessageBox,
                                QHeaderView, QMenu, QDockWidget)
 from nexxT.interface import Services, FilterState
 from nexxT.core.Configuration import Configuration
 from nexxT.core.Application import Application
-from nexxT.core.Utils import assertMainThread, MethodInvoker, mainThread
+from nexxT.core.Utils import assertMainThread, MethodInvoker, mainThread, handleException
 from nexxT.services.SrvConfiguration import MVCConfigurationBase, ConfigurationModel, ITEM_ROLE
 from nexxT.services.gui.PropertyDelegate import PropertyDelegate
 from nexxT.services.gui.GraphEditor import GraphScene
@@ -57,16 +58,16 @@ def __init__(self, configuration):
 
         self.actReload = QAction(QIcon.fromTheme("browser-reload", style.standardIcon(QStyle.SP_BrowserReload)),
                                  "Reload python", self)
-        self.actReload.setShortcut(QKeySequence(Qt.CTRL + Qt.Key_P))
+        self.actReload.setShortcut(QKeySequence(Qt.CTRL | Qt.Key_P))
         self.actReload.triggered.connect(self._execReload)
 
         self.actActivate = QAction(QIcon.fromTheme("arrow-up", style.standardIcon(QStyle.SP_ArrowUp)),
                                    "Initialize", self)
         self.actActivate.triggered.connect(self.activate)
-        self.actActivate.setShortcut(QKeySequence(Qt.CTRL + Qt.Key_I))
+        self.actActivate.setShortcut(QKeySequence(Qt.CTRL | Qt.Key_I))
         self.actDeactivate = QAction(QIcon.fromTheme("arrow-down", style.standardIcon(QStyle.SP_ArrowDown)),
                                      "Deinitialize", self)
-        self.actDeactivate.setShortcut(QKeySequence(Qt.CTRL + Qt.Key_D))
+        self.actDeactivate.setShortcut(QKeySequence(Qt.CTRL | Qt.Key_D))
         self.actDeactivate.triggered.connect(self.deactivate)
 
         confMenu.addAction(self.actLoad)
@@ -84,7 +85,7 @@ def __init__(self, configuration):
         toolBar.addAction(self.actDeactivate)
 
         self.recentConfigs = [QAction() for i in range(10)]
-        self.recentConfigs[0].setShortcut(QKeySequence(Qt.CTRL + Qt.Key_R))
+        self.recentConfigs[0].setShortcut(QKeySequence(Qt.CTRL | Qt.Key_R))
         confMenu.addSeparator()
         recentMenu = confMenu.addMenu("Recent")
         for a in self.recentConfigs:
@@ -96,7 +97,7 @@ def __init__(self, configuration):
         self.treeView = QTreeView(self.mainWidget)
         self.treeView.setHeaderHidden(False)
         self.treeView.setSelectionMode(QAbstractItemView.NoSelection)
-        self.treeView.setEditTriggers(self.treeView.EditKeyPressed|self.treeView.AnyKeyPressed)
+        self.treeView.setEditTriggers(self.treeView.EditTrigger.EditKeyPressed|self.treeView.EditTrigger.AnyKeyPressed)
         self.treeView.setAllColumnsShowFocus(True)
         self.treeView.setExpandsOnDoubleClick(False)
         self.treeView.setDragEnabled(True)
@@ -210,7 +211,7 @@ def _addGraphView(self, subConfig):
         # remove already deleted graph views from internal list
         valid_graphViews = []
         for gv in self._graphViews:
-            if shiboken2.isValid(gv): # pylint: disable=no-member
+            if nexxT.shiboken.isValid(gv): # pylint: disable=no-member
                 valid_graphViews.append(gv)
         self._graphViews = valid_graphViews
         # check if graph view is already there
@@ -220,7 +221,7 @@ def _addGraphView(self, subConfig):
                 return
         # create new graph view
         srv = Services.getService("MainWindow")
-        graphDw = srv.newDockWidget("Graph (%s)" % (subConfig.getName()), parent=None,
+        graphDw = srv.newDockWidget(f"Graph ({subConfig.getName()})", parent=None,
                                     defaultArea=Qt.RightDockWidgetArea,
                                     allowedArea=Qt.RightDockWidgetArea | Qt.BottomDockWidgetArea)
         graphDw.setAttribute(Qt.WA_DeleteOnClose, True)
@@ -232,10 +233,19 @@ def _addGraphView(self, subConfig):
         graphDw.visibleChanged.connect(self._removeGraphViewFromList)
 
     def _subConfigRemoved(self, subConfigName, configType):
+        self.__subConfigRemoved(subConfigName, configType)
+
+    @handleException
+    def __subConfigRemoved(self, subConfigName, configType):
         g = self._configuration.subConfigByNameAndTye(subConfigName, configType).getGraph()
-        for gv in self._graphViews:
-            if shiboken2.isValid(gv):  # pylint: disable=no-member
-                if gv.widget().scene().graph == g:
+        if nexxT.shiboken.isValid(g):
+            for gv in self._graphViews:
+                if not nexxT.shiboken.isValid(gv):
+                    continue
+                if (not nexxT.shiboken.isValid(gv.widget()) or
+                        not nexxT.shiboken.isValid(gv.widget().scene()) or
+                        not nexxT.shiboken.isValid(gv.widget().scene().graph) or
+                        gv.widget().scene().graph == g):
                     logger.debug("deleting graph view for subconfig %s", subConfigName)
                     gv.deleteLater()
 
@@ -284,13 +294,14 @@ def _execTreeViewContextMenu(self, point):
                         s2.append(aseq)
                 m2.addActions(s1)
                 m3.addActions(s2)
-            m.exec_(self.treeView.mapToGlobal(point))
+            nexxT.Qt.call_exec(m, self.treeView.mapToGlobal(point))
             return
         if self.model.isSubConfigParent(index) == Configuration.CONFIG_TYPE_APPLICATION:
             m = QMenu()
             a = QAction("Add application")
             m.addAction(a)
-            a = m.exec_(self.treeView.mapToGlobal(point))
+            point = self.treeView.mapToGlobal(point)
+            a = nexxT.Qt.call_exec(m, point)
             if a is not None:
                 self._configuration.addNewApplication()
             return
@@ -298,7 +309,7 @@ def _execTreeViewContextMenu(self, point):
             m = QMenu()
             a = QAction("Add composite filter")
             m.addAction(a)
-            a = m.exec_(self.treeView.mapToGlobal(point))
+            a = nexxT.Qt.call_exec(m, self.treeView.mapToGlobal(point))
             if a is not None:
                 self._configuration.addNewCompositeFilter()
             return
@@ -394,7 +405,7 @@ def _disconnectSingleShotPlay(self):
         pbsrv = Services.getService("PlaybackControl")
         try:
             pbsrv.playbackPaused.disconnect(self._singleShotPlay)
-        except RuntimeError:
+        except (RuntimeError, AttributeError):
             # we are already disconnected.
             pass
 
diff --git a/nexxT/services/gui/GraphEditor.py b/nexxT/services/gui/GraphEditor.py
index 5874e38..6bf4c59 100644
--- a/nexxT/services/gui/GraphEditor.py
+++ b/nexxT/services/gui/GraphEditor.py
@@ -12,12 +12,13 @@
 import platform
 import os.path
 import pkg_resources
-from PySide2.QtWidgets import (QGraphicsScene, QGraphicsItemGroup, QGraphicsSimpleTextItem,
-                               QGraphicsPathItem, QGraphicsItem, QMenu, QAction, QInputDialog, QMessageBox,
+import nexxT.Qt
+from nexxT.Qt.QtWidgets import (QGraphicsScene, QGraphicsItemGroup, QGraphicsSimpleTextItem,
+                               QGraphicsPathItem, QGraphicsItem, QMenu, QInputDialog, QMessageBox,
                                QGraphicsLineItem, QFileDialog, QDialog, QGridLayout, QCheckBox, QVBoxLayout, QGroupBox,
                                QDialogButtonBox, QGraphicsView, QStyle, QStyleOptionGraphicsItem)
-from PySide2.QtGui import QBrush, QPen, QColor, QPainterPath, QImage
-from PySide2.QtCore import QPointF, Signal, QObject, QRectF, QSizeF, Qt
+from nexxT.Qt.QtGui import QBrush, QPen, QColor, QPainterPath, QImage, QAction
+from nexxT.Qt.QtCore import QPointF, Signal, QObject, QRectF, QSizeF, Qt
 from nexxT.core.BaseGraph import BaseGraph
 from nexxT.core.Graph import FilterGraph
 from nexxT.core.SubConfiguration import SubConfiguration
@@ -207,8 +208,8 @@ def nodeHeight(self):
             style = BaseGraphScene.getData if self.scene() is None else self.scene().getData
             size = style(self, BaseGraphScene.STYLE_ROLE_SIZE)
             vspacing = style(self, BaseGraphScene.STYLE_ROLE_VSPACING)
-            inPortHeight = sum([style(ip, BaseGraphScene.STYLE_ROLE_VSPACING) for ip in self.inPortItems])
-            outPortHeight = sum([style(op, BaseGraphScene.STYLE_ROLE_VSPACING) for op in self.outPortItems])
+            inPortHeight = sum(style(ip, BaseGraphScene.STYLE_ROLE_VSPACING) for ip in self.inPortItems)
+            outPortHeight = sum(style(op, BaseGraphScene.STYLE_ROLE_VSPACING) for op in self.outPortItems)
             nodeHeight = size.height() + max(inPortHeight, outPortHeight)
             return nodeHeight+2*vspacing
 
@@ -236,8 +237,8 @@ def sync(self):
             hspacing = style(self, BaseGraphScene.STYLE_ROLE_HSPACING)
             radius = style(self, BaseGraphScene.STYLE_ROLE_RRRADIUS)
 
-            inPortHeight = sum([style(ip, BaseGraphScene.STYLE_ROLE_VSPACING) for ip in self.inPortItems])
-            outPortHeight = sum([style(op, BaseGraphScene.STYLE_ROLE_VSPACING) for op in self.outPortItems])
+            inPortHeight = sum(style(ip, BaseGraphScene.STYLE_ROLE_VSPACING) for ip in self.inPortItems)
+            outPortHeight = sum(style(op, BaseGraphScene.STYLE_ROLE_VSPACING) for op in self.outPortItems)
             nodeHeight = size.height() + max(inPortHeight, outPortHeight)
             nodePP.addRoundedRect(hspacing, vspacing, size.width(), nodeHeight, radius, radius)
             if not hasattr(self, "nodeGrItem"):
@@ -983,7 +984,7 @@ def getSelectedPorts(parent, inputPorts, outputPorts, graph, nodeName):
         :param nodeName: the name of the corresponding node
         """
         d = PortSelectorDialog(parent, inputPorts, outputPorts, graph, nodeName)
-        if d.exec() == QDialog.Accepted:
+        if nexxT.Qt.call_exec(d) == QDialog.Accepted:
             return d.selectedInputPorts, d.selectedOutputPorts
         return [], []
 
@@ -1027,9 +1028,15 @@ def __init__(self, graph, parent):
         self.actAddNode = QAction("Add filter from file ...", self)
         self.actAutoLayout = QAction("Auto layout", self)
         self.actRemoveConnection = QAction("Remove connection ...", self)
+        self.actSetNonblockingConnection = QAction("Set non blocking", self)
+        self.actSetStandardBlockingConnection = QAction("Set blocking", self)
+        self.actSetCustomBlockingConnection = QAction("Set blocking with width ...", self)
         self.actRenameNode.triggered.connect(self.renameDialog)
         self.actRemoveNode.triggered.connect(self.removeDialog)
         self.actRemoveConnection.triggered.connect(self.onConnectionRemove)
+        self.actSetNonblockingConnection.triggered.connect(self.onConnSetNonBlocking)
+        self.actSetStandardBlockingConnection.triggered.connect(self.onConnSetBlocking)
+        self.actSetCustomBlockingConnection.triggered.connect(self.onConnSetCustom)
         self.actAutoLayout.triggered.connect(self.autoLayout)
         if isinstance(self.graph, FilterGraph):
             self.actRenamePort = QAction("Rename dynamic port ...", self)
@@ -1037,7 +1044,7 @@ def __init__(self, graph, parent):
             self.actAddInputPort = QAction("Add dynamic input port ...", self)
             self.actAddOutputPort = QAction("Add dynamic output port ...", self)
             self.actSuggestDynamicPorts = QAction("Suggest dynamic ports ...", self)
-            self.entryPointActions = dict()
+            self.entryPointActions = {}
             for ep in pkg_resources.iter_entry_points("nexxT.filters"):
                 d = self.entryPointActions
                 groups = ep.name.split(".")
@@ -1050,7 +1057,7 @@ def __init__(self, graph, parent):
                 groups = groups[:-1]
                 for g in groups:
                     if not g in d:
-                        d[g] = dict()
+                        d[g] = {}
                     d = d[g]
                 if name in d:
                     logger.warning("Entry point '%s' registered twice, ignoring duplicates", ep.name)
@@ -1103,10 +1110,26 @@ def getData(self, item, role):
                 if len(threads) > 1:
                     return QBrush(QColor(255, 255, 255, 200))
                 return QBrush(QColor(255, 255, 255, 100))
+        if isinstance(item, BaseGraphScene.ConnectionItem) and isinstance(self.graph, FilterGraph):
+            if role == BaseGraphScene.STYLE_ROLE_PEN:
+                pen = BaseGraphScene.getData(item, role)
+                assert isinstance(pen, QPen)
+                nodeFrom = item.portFrom.nodeItem.name
+                portFrom = item.portFrom.name
+                nodeTo = item.portTo.nodeItem.name
+                portTo = item.portTo.name
+                width = self.graph.getConnectionProperties(nodeFrom, portFrom, nodeTo, portTo)["width"]
+                if width == 0:
+                    pen.setColor(QColor.fromString("red"))
+                elif width > 1:
+                    pen.setColor(QColor.fromString("blue"))
+                return pen
         return BaseGraphScene.getData(item, role)
 
-
     def contextMenuEvent(self, event):
+        """
+        Overwritten from QGraphicsScene.
+        """
         item = self.graphItemAt(event.scenePos())
         self.itemOfContextMenu = item
         if isinstance(item, BaseGraphScene.NodeItem):
@@ -1127,7 +1150,7 @@ def contextMenuEvent(self, event):
                     self.actSetThread.setEnabled(False)
                 else:
                     self.actSetThread.setEnabled(True)
-            m.exec_(event.screenPos())
+            nexxT.Qt.call_exec(m, event.screenPos())
         elif isinstance(item, BaseGraphScene.PortItem):
             m = QMenu(self.views()[0])
             if isinstance(self.graph, FilterGraph):
@@ -1136,11 +1159,14 @@ def contextMenuEvent(self, event):
                 self.actRenamePort.setEnabled(port.dynamic())
                 self.actRemovePort.setEnabled(port.dynamic())
             m.addActions([self.actRenamePort, self.actRemovePort])
-            m.exec_(event.screenPos())
+            nexxT.Qt.call_exec(m, event.screenPos())
         elif isinstance(item, BaseGraphScene.ConnectionItem):
             m = QMenu(self.views()[0])
-            m.addActions([self.actRemoveConnection])
-            m.exec_(event.screenPos())
+            m.addActions([self.actSetNonblockingConnection,
+                          self.actSetStandardBlockingConnection,
+                          self.actSetCustomBlockingConnection,
+                          self.actRemoveConnection])
+            nexxT.Qt.call_exec(m, event.screenPos())
         else:
             self.itemOfContextMenu = event.scenePos()
             m = QMenu(self.views()[0])
@@ -1156,7 +1182,7 @@ def populate(menu, src):
                         menu.addAction(src[k])
             populate(flm, self.entryPointActions)
             m.addAction(self.actAutoLayout)
-            m.exec_(event.screenPos())
+            nexxT.Qt.call_exec(m, event.screenPos())
         self.itemOfContextMenu = None
 
     def compositeFilters(self):
@@ -1228,6 +1254,27 @@ def onConnectionRemove(self):
         self.graph.deleteConnection(item.portFrom.nodeItem.name, item.portFrom.name,
                                     item.portTo.nodeItem.name, item.portTo.name)
 
+    def onConnSetNonBlocking(self):
+        item = self.itemOfContextMenu
+        self.graph.setConnectionProperties(item.portFrom.nodeItem.name, item.portFrom.name,
+                                           item.portTo.nodeItem.name, item.portTo.name, dict(width=0))
+        item.sync()
+
+    def onConnSetBlocking(self):
+        item = self.itemOfContextMenu
+        self.graph.setConnectionProperties(item.portFrom.nodeItem.name, item.portFrom.name,
+                                           item.portTo.nodeItem.name, item.portTo.name, dict(width=1))
+        item.sync()
+
+    def onConnSetCustom(self):
+        item = self.itemOfContextMenu
+        c = item.portFrom.nodeItem.name, item.portFrom.name, item.portTo.nodeItem.name, item.portTo.name
+        width = self.graph.getConnectionProperties(*c)["width"]
+        width, ok = QInputDialog.getInt(self.views()[0], self.sender().text(), "Enter connection width", width, 1)
+        if ok:
+            self.graph.setConnectionProperties(*(c + (dict(width=width),)))
+            item.sync()
+
     def addInputPort(self):
         """
         Adds an input port to a node
@@ -1326,8 +1373,7 @@ def onAddFilterFromFile(self):
             suff = "*.so"
         else:
             suff = "*.dll"
-        library, ok = QFileDialog.getOpenFileName(self.views()[0], "Choose Library",
-                                                  filter="Filters (*.py %s)" % suff)
+        library, ok = QFileDialog.getOpenFileName(self.views()[0], "Choose Library", filter=f"Filters (*.py {suff})")
         if not (ok and library is not None and os.path.exists(library)):
             return
         if library.endswith(".py"):
diff --git a/nexxT/services/gui/GraphEditorView.py b/nexxT/services/gui/GraphEditorView.py
index ced1e1c..f17a760 100644
--- a/nexxT/services/gui/GraphEditorView.py
+++ b/nexxT/services/gui/GraphEditorView.py
@@ -10,9 +10,9 @@
 
 import logging
 import json
-from PySide2.QtCore import QMimeData
-from PySide2.QtGui import QPainter, QKeySequence, QGuiApplication
-from PySide2.QtWidgets import QGraphicsView
+from nexxT.Qt.QtCore import QMimeData
+from nexxT.Qt.QtGui import QPainter, QKeySequence, QGuiApplication
+from nexxT.Qt.QtWidgets import QGraphicsView
 from nexxT.core.SubConfiguration import SubConfiguration
 from nexxT.services.gui.GraphEditor import BaseGraphScene, GraphScene
 
@@ -77,7 +77,7 @@ def _copy(self, cut):
             saved["nodes"] = saved["nodes"][:i] + saved["nodes"][i+1:]
         cToDel = set()
         for c in saved["connections"]:
-            node1, _, node2, _ = SubConfiguration.connectionStringToTuple(c)
+            node1, _, node2, _, _ = SubConfiguration.connectionStringToTuple(c)
             if node1 in deletedNodes or node2 in deletedNodes:
                 cToDel.add(c)
         for c in cToDel:
@@ -107,10 +107,10 @@ def _paste(self):
                 n["name"] = nameTransformations[n["name"]]
             newConn = []
             for c in cfg["connections"]:
-                node1, port1, node2, port2 = SubConfiguration.connectionStringToTuple(c)
+                node1, port1, node2, port2, props = SubConfiguration.connectionStringToTuple(c)
                 node1 = nameTransformations[node1]
                 node2 = nameTransformations[node2]
-                newConn.append(SubConfiguration.tupleToConnectionString((node1, port1, node2, port2)))
+                newConn.append(SubConfiguration.tupleToConnectionString((node1, port1, node2, port2, props)))
             cfg["connections"] = newConn
             def compositeLookup(name):
                 return self.scene().graph.getSubConfig().getConfiguration().compositeFilterByName(name)
diff --git a/nexxT/services/gui/GraphLayering.py b/nexxT/services/gui/GraphLayering.py
index 8b61e97..328ae8a 100644
--- a/nexxT/services/gui/GraphLayering.py
+++ b/nexxT/services/gui/GraphLayering.py
@@ -73,9 +73,9 @@ def dump(self, title=None): # pragma: no cover
         """
         if title is not None:
             print(title)
-        for n1 in self.dgForward:
+        for n1, fwd in self.dgForward.items():
             print(n1, end=": ")
-            for n2 in self.dgForward[n1]:
+            for n2 in fwd:
                 if (n1, n2) not in self.cycleEdges:
                     print(n2, end=",")
             print()
@@ -192,7 +192,7 @@ def numberOfCrossings(layer1, layer2):
                     self.dgForward[nc].add(n2)
                     self.dgBackward[n2].add(nc)
         #self.dump("after adding virtual nodes")
-        nc = sum([numberOfCrossings(layers[i-1], layers[i]) for i in range(1, len(layers))])
+        nc = sum(numberOfCrossings(layers[i-1], layers[i]) for i in range(1, len(layers)))
         #print("numCrosses before heuristic:", nc)
         # heuristic for rearranging the layer according to the average position of previous nodes
         numCrosses = 0
@@ -230,7 +230,7 @@ def getNumCrosses(cLayer):
                         layers[cl] = testL
                 if not found:
                     break
-        numCrosses = sum([numberOfCrossings(layers[i-1], layers[i]) for i in range(1, len(layers))])
+        numCrosses = sum(numberOfCrossings(layers[i-1], layers[i]) for i in range(1, len(layers)))
         return layers, numCrosses
 
     def layersToNodeNames(self, layers):
diff --git a/nexxT/services/gui/GuiLogger.py b/nexxT/services/gui/GuiLogger.py
index de7d1e9..dad4a56 100644
--- a/nexxT/services/gui/GuiLogger.py
+++ b/nexxT/services/gui/GuiLogger.py
@@ -11,10 +11,11 @@
 from queue import Queue
 import traceback
 import logging
-import shiboken2
-from PySide2.QtCore import Qt, QTimer, QAbstractItemModel, QModelIndex, Slot
-from PySide2.QtWidgets import QTableView, QHeaderView, QAction, QActionGroup
-from PySide2.QtGui import QColor
+import nexxT.shiboken
+from nexxT.Qt import __version_info__ as qtversion
+from nexxT.Qt.QtCore import Qt, QTimer, QAbstractItemModel, QModelIndex, Slot
+from nexxT.Qt.QtWidgets import QTableView, QHeaderView, QStyleOptionViewItem
+from nexxT.Qt.QtGui import QColor, QAction, QActionGroup
 from nexxT.services.ConsoleLogger import ConsoleLogger
 from nexxT.interface import Services
 from nexxT.core.Utils import assertMainThread
@@ -189,7 +190,7 @@ def data(self, modelIndex, role):
                 return e[modelIndex.column()]
             if role == Qt.ToolTipRole:
                 return e[2]
-            if role == Qt.BackgroundColorRole:
+            if role == Qt.BackgroundRole:
                 levelno = e[1]
                 if levelno <= logging.INTERNAL:
                     return QColor(255, 255, 255)  # white
@@ -240,7 +241,7 @@ def setUniformRowHeights(self, enabled):
             if enabled:
                 func = self.sizeHintForRow
             else:
-                func = lambda r: max(self._getCellHeights(r))
+                func = lambda r: max(self._getCellHeights(r)) # pylint: disable=unnecessary-lambda-assignment
             for r in range(self._model.rowCount(QModelIndex())):
                 self.setRowHeight(r, func(r))
             if self.follow:
@@ -278,7 +279,11 @@ def changeEvent(self, event):
 
     def _getCellHeights(self, row=0):
         self.ensurePolished()
-        option = self.viewOptions()
+        if qtversion[0] == 5:
+            option = self.viewOptions()
+        else:
+            option = QStyleOptionViewItem()
+            self.initViewItemOption(option)
         model = self._model
         for column in range(model.columnCount(QModelIndex())):
             index = model.index(row, column, QModelIndex())
@@ -302,7 +307,7 @@ def update(self):
         :return: None
         """
         assertMainThread()
-        if not shiboken2.isValid(self): # pylint: disable=no-member
+        if not nexxT.shiboken.isValid(self): # pylint: disable=no-member
             return
         if not self.queue.empty():
             self._model.update(self.queue)
diff --git a/nexxT/services/gui/MainWindow.py b/nexxT/services/gui/MainWindow.py
index d04847c..11f713d 100644
--- a/nexxT/services/gui/MainWindow.py
+++ b/nexxT/services/gui/MainWindow.py
@@ -10,14 +10,14 @@
 import logging
 import re
 import subprocess
-import sys
 import time
-import shiboken2
-from PySide2.QtWidgets import (QMainWindow, QMdiArea, QMdiSubWindow, QDockWidget, QAction, QWidget, QGridLayout,
-                               QMenuBar, QMessageBox, QScrollArea, QLabel, QActionGroup)
-from PySide2.QtCore import (QObject, Signal, Slot, Qt, QByteArray, QDataStream, QIODevice, QRect, QPoint, QSettings,
+import sys
+import nexxT.shiboken
+from nexxT.Qt.QtWidgets import (QMainWindow, QMdiArea, QMdiSubWindow, QDockWidget, QWidget, QGridLayout,
+                               QMenuBar, QMessageBox, QScrollArea, QLabel)
+from nexxT.Qt.QtCore import (QObject, Signal, Slot, Qt, QByteArray, QDataStream, QIODevice, QRect, QPoint, QSettings,
                             QTimer, QUrl, QEvent)
-from PySide2.QtGui import QDesktopServices
+from nexxT.Qt.QtGui import QDesktopServices, QAction, QActionGroup
 import nexxT
 from nexxT.interface import Filter
 from nexxT.core.Application import Application
@@ -78,8 +78,8 @@ def saveGeometry(self):
         stream.writeInt64(normalGeom.y())
         stream.writeInt64(normalGeom.width())
         stream.writeInt64(normalGeom.height())
-        stream.writeUInt32(self.windowState() & Qt.WindowMaximized)
-        stream.writeUInt32(self.windowState() & Qt.WindowFullScreen)
+        stream.writeUInt32(int(bool(self.windowState() & Qt.WindowMaximized)))
+        stream.writeUInt32(int(bool(self.windowState() & Qt.WindowFullScreen)))
         return array
 
     def restoreGeometry(self, geometry):
@@ -107,8 +107,8 @@ def restoreGeometry(self, geometry):
         width = stream.readInt64()
         height = stream.readInt64()
         restoredNormalGeometry = QRect(x, y, width, height)
-        maximized = stream.readUInt32()
-        fullScreen = stream.readUInt32()
+        maximized = bool(stream.readUInt32())
+        fullScreen = bool(stream.readUInt32())
         frameHeight = 20
         if not restoredFrameGeometry.isValid():
             restoredFrameGeometry = QRect(QPoint(0, 0), self.sizeHint())
@@ -182,7 +182,7 @@ def __init__(self, config):
         for framerate in [1, 2, 5, 10, 15, 20, 25, 40, 50, 100]:
             a = QAction(self)
             a.setData(framerate)
-            a.setText("%d Hz" % framerate)
+            a.setText(f"{framerate} Hz")
             a.setCheckable(True)
             if framerate == self.framerate:
                 a.setChecked(True)
@@ -203,13 +203,13 @@ def __init__(self, config):
         m.addSeparator()
         m.addActions([self.aboutNexxT, self.aboutQt, self.aboutPython])
         self.helpNexxT.triggered.connect(lambda: QDesktopServices.openUrl(QUrl("https://nexxT.readthedocs.org")))
-        self.aboutNexxT.triggered.connect(lambda: QMessageBox.about(self, "About nexxT", """\
-This program uses <b>nexxT</b> %(version)s, a generic hybrid python/c++ framework for developing computer vision 
+        self.aboutNexxT.triggered.connect(lambda: QMessageBox.about(self, "About nexxT", f"""\
+This program uses <b>nexxT</b> {nexxT.__version__}, a generic hybrid python/c++ framework for developing computer vision 
 algorithms.<br><br>
 
 nexxT is available under the <a href='https://github.com/ifm/nexxT/blob/master/LICENSE'>Apache 2.0 License</a> together 
 with the <a href='https://github.com/ifm/nexxT/blob/master/NOTICE'>notice</a>. 
-""" % dict(version=nexxT.__version__)))
+"""))
         self.aboutQt.triggered.connect(lambda: QMessageBox.aboutQt(self))
         self.aboutPython.triggered.connect(self._aboutPython)
         self.toolbar = None
@@ -225,6 +225,9 @@ def __init__(self, config):
         self._deferredUpdateTimer.timeout.connect(self._deferredUpdateTimeout)
 
     def eventFilter(self, obj, event):
+        """
+        Overwritten from QObject, for disabling mouse wheel scrolling on the main widget's viewport
+        """
         if obj is self.mdi.viewport() and event.type() == QEvent.Wheel:
             # disable mouse wheel scrolling on the viewport widget it's pretty annoying...
             return True
@@ -356,7 +359,7 @@ def saveMdiState(self):
             prefix = i["prefix"]
             logger.debug("save window geometry %s: %s", prefix, window.geometry())
             geom = str(window.saveGeometry().toBase64(), "ascii")
-            visible = self.windows[shiboken2.getCppPointer(window)[0]].isChecked() # pylint: disable=no-member
+            visible = self.windows[nexxT.shiboken.getCppPointer(window)[0]].isChecked() # pylint: disable=no-member
             propColl.setProperty(prefix + "_geom", geom)
             logger.debug("%s is visible: %d", prefix, int(visible))
             propColl.setProperty(prefix + "_visible", int(visible))
@@ -467,14 +470,14 @@ def subplot(self, windowId, theFilter, widget):
         """
         logger.internal("subplot '%s'", windowId)
         title, row, col = self.parseWindowId(windowId)
-        title = self._substWindowTitle(title, theFilter)        
+        title = self._substWindowTitle(title, theFilter)
         if title in self.managedSubplots and (row, col) in self.managedSubplots[title]["plots"]:
             logger.warning("subplot %s[%d,%d] is already registered. Creating a new window for the plot.",
                            title, row, col)
             i = 2
-            while "%s(%d)" % (title, i) in self.managedSubplots:
+            while f"{title}({i})" in self.managedSubplots:
                 i += 1
-            title = "%s(%d)" % (title, i)
+            title = f"{title}({i})"
             row = 0
             col = 0
         if title not in self.managedSubplots:
@@ -492,7 +495,7 @@ def subplot(self, windowId, theFilter, widget):
         #       with the 100 ms timeout this couldn't be reproduced
         QTimer.singleShot(100, lambda: (
             self.managedSubplots[title]["mdiSubWindow"].adjustSize() if
-            shiboken2.isValid(widget) and ( # pylint: disable=no-member
+            nexxT.shiboken.isValid(widget) and ( # pylint: disable=no-member
                 widget.parent().size().height() < widget.minimumSizeHint().height() or
                 widget.parent().size().height() < widget.minimumSize().height()) else None
         ))
@@ -520,9 +523,9 @@ def releaseSubplot(self, arg):
         elif isinstance(arg, QWidget):
             widget = arg
             found = False
-            for title in self.managedSubplots:
-                for row, col in self.managedSubplots[title]["plots"]:
-                    if self.managedSubplots[title]["plots"][row, col] is widget:
+            for title, sp in self.managedSubplots.items():
+                for row, col in sp["plots"]:
+                    if sp["plots"][row, col] is widget:
                         found = True
                         break
                 if found:
@@ -605,15 +608,15 @@ def ensureVisible():
         act.triggered.connect(ensureVisible)
         window.visibleChanged.connect(act.setChecked)
         nameChangedSignal.connect(act.setText)
-        self.windows[shiboken2.getCppPointer(window)[0]] = act # pylint: disable=no-member
+        self.windows[nexxT.shiboken.getCppPointer(window)[0]] = act # pylint: disable=no-member
         self.menu.addAction(act)
         logger.debug("Registering window %s, new len=%d",
-                     shiboken2.getCppPointer(window), len(self.windows)) # pylint: disable=no-member
+                     nexxT.shiboken.getCppPointer(window), len(self.windows)) # pylint: disable=no-member
         window.destroyed.connect(self._windowDestroyed)
 
     def _windowDestroyed(self, obj):
         logger.internal("_windowDestroyed")
-        ptr = shiboken2.getCppPointer(obj) # pylint: disable=no-member
+        ptr = nexxT.shiboken.getCppPointer(obj) # pylint: disable=no-member
         try:
             ptr = ptr[0]
         except TypeError:
@@ -630,7 +633,7 @@ def _appActivated(self, name, app):
         else:
             self.activeApp = None
 
-    def _aboutPython(self): # pylint: disable=no-self-use
+    def _aboutPython(self):
         piplic = subprocess.check_output([sys.executable, "-m", "piplicenses", "--format=plain"],
                                          encoding="utf-8").replace("\n", "<br>").replace(" ", "&nbsp;")
         piplic = piplic.replace("<br>", "<br><br>", 1)
diff --git a/nexxT/services/gui/PlaybackControl.py b/nexxT/services/gui/PlaybackControl.py
index 52e0b6c..d359a15 100644
--- a/nexxT/services/gui/PlaybackControl.py
+++ b/nexxT/services/gui/PlaybackControl.py
@@ -10,10 +10,9 @@
 import functools
 import logging
 from pathlib import Path
-from PySide2.QtCore import Signal, Qt, QTimer, QDir
-from PySide2.QtGui import QIcon
-from PySide2.QtWidgets import (QWidget, QGridLayout, QLabel, QBoxLayout, QSlider, QToolBar, QAction, QApplication,
-                               QStyle, QActionGroup)
+from nexxT.Qt.QtCore import Signal, Qt, QTimer, QDir
+from nexxT.Qt.QtGui import QIcon, QAction, QActionGroup
+from nexxT.Qt.QtWidgets import QWidget, QGridLayout, QLabel, QBoxLayout, QSlider, QToolBar, QApplication, QStyle
 from nexxT.interface import Services
 from nexxT.core.Exceptions import PropertyCollectionPropertyNotFound
 from nexxT.core.Utils import assertMainThread
@@ -62,7 +61,7 @@ def __init__(self, config):
         self.actSeekBegin = QAction(QIcon.fromTheme("media-skip-backward",
                                                     style.standardIcon(QStyle.SP_MediaSkipBackward)),
                                     "Seek Begin", self)
-        self.actSetTimeFactor = {r : QAction("x 1/%d" % (1/r), self) if r < 1 else QAction("x %d" % r, self)
+        self.actSetTimeFactor = {r : QAction(f"x 1/{1//r}", self) if r < 1 else QAction(f"x {r}", self)
                                  for r in (1/8, 1/4, 1/2, 1, 2, 4, 8)}
 
         # pylint: disable=unnecessary-lambda
@@ -267,6 +266,7 @@ def _splitTime(milliseconds):
 
     @staticmethod
     def _timeToString(milliseconds):
+        # pylint: disable=consider-using-f-string
         return "%02d:%02d:%02d.%03d" % (MVCPlaybackControlGUI._splitTime(milliseconds))
 
     def _currentTimestampChanged(self, currentTime):
@@ -402,9 +402,9 @@ def _timeRatioChanged(self, newRatio):
         logger.debug("new timeRatio: %f", newRatio)
         for r in [1/8, 1/4, 1/2, 1, 2, 4, 8]:
             if abs(newRatio / r - 1) < 0.01:
-                self.timeRatioLabel.setText(("x 1/%d"%(1/r)) if r < 1 else ("x %d"%r))
+                self.timeRatioLabel.setText((f"x 1/{1//r}") if r < 1 else (f"x {r}"))
                 return
-        self.timeRatioLabel.setText("%.2f" % newRatio)
+        self.timeRatioLabel.setText(f"{newRatio:.2f}")
         super()._timeRatioChanged(newRatio)
 
     def selectedStream(self):
diff --git a/nexxT/services/gui/Profiling.py b/nexxT/services/gui/Profiling.py
index 5070059..8b65596 100644
--- a/nexxT/services/gui/Profiling.py
+++ b/nexxT/services/gui/Profiling.py
@@ -10,9 +10,9 @@
 
 import logging
 import numpy as np
-from PySide2.QtCore import QByteArray, Slot, Qt, QPointF, QLineF, QRectF, QEvent
-from PySide2.QtGui import QPainter, QPolygonF, QPen, QColor, QFontMetricsF, QPalette
-from PySide2.QtWidgets import QWidget, QToolTip, QAction
+from nexxT.Qt.QtCore import QByteArray, Slot, Qt, QPointF, QLineF, QRectF, QEvent
+from nexxT.Qt.QtGui import QPainter, QPolygonF, QPen, QColor, QFontMetricsF, QPalette, QAction
+from nexxT.Qt.QtWidgets import QWidget, QToolTip
 from nexxT.core.Utils import ThreadToColor
 from nexxT.interface import Services
 from nexxT.services.SrvProfiling import ProfilingService
@@ -75,17 +75,17 @@ def removeThread(self, thread):
         if thread in self._loadData:
             del self._loadData[thread]
 
-    def paintEvent(self, ev):
+    def paintEvent(self, event):
         """
         Manually implemented paint event
 
-        :param ev: the QT paint event
+        :param event: the QT paint event
         :return:
         """
         h = self.height()
         w = self.width()
         p = QPainter(self)
-        p.setClipRect(ev.region().boundingRect())
+        p.setClipRect(event.region().boundingRect())
         pen = QPen(QColor(0, 0, 0))
         pen.setWidth(4)
         ls = QFontMetricsF(p.font()).lineSpacing()
@@ -99,7 +99,7 @@ def paintEvent(self, ev):
             p.drawText(QPointF(35, y), t)
 
         if len(self._loadData) > 0:
-            right = max([polygon[polygon.count()-1].x() for _, polygon in self._loadData.items()])
+            right = max(polygon[polygon.count()-1].x() for _, polygon in self._loadData.items())
         else:
             right = 0.0
         p.translate(w-10-right*20, h-10)
@@ -173,18 +173,18 @@ def removeThread(self, thread):
         if thread in self._spanData:
             self._removedThreads.add(thread)
 
-    def paintEvent(self, ev):
+    def paintEvent(self, event):
         """
         Manually implemented paint event of the time / occupancy diagram.
 
-        :param ev: the qt paint event
+        :param event: the qt paint event
         :return:
         """
         bgcolor = self.palette().color(self.backgroundRole())
         h = self.height()
         w = self.width()
         p = QPainter(self)
-        p.setClipRect(ev.region().boundingRect())
+        p.setClipRect(event.region().boundingRect())
         pen = QPen(QColor(0, 0, 0))
         pen.setWidth(0)
         pen.setCosmetic(True)
@@ -248,9 +248,9 @@ def textDescription(self, thread, port):
         :return: a string instance containing the profiling info.
         """
         sd = self._spanData[thread][port]
-        res = "Thread: %s, Port: %s\n" % (thread, port)
+        res = f"Thread: {thread}, Port: {port}\n"
         groups = []
-        activeGroup = dict()
+        activeGroup = {}
         for i in range(sd.shape[0]):
             if len(activeGroup) > 0:
                 if sd[i, 1] <= activeGroup["finish"]:
@@ -260,27 +260,27 @@ def textDescription(self, thread, port):
             activeGroup = dict(start=sd[i, 0], finish=sd[i, 1], subcalls=[])
         for i, g in enumerate(groups[::-1]):
             total = (g["finish"] - g["start"])*1e-6
-            exclusive = sum([sc[1]-sc[0] for sc in g["subcalls"]])*1e-6
+            exclusive = sum(sc[1]-sc[0] for sc in g["subcalls"])*1e-6
             subcalls = total - exclusive
-            res += " event[%d] Total runtime: %.1f ms; Exclusive time: %.1f ms; Subcall time: %.1f ms\n" % (
-                -i-1, total, exclusive, subcalls)
+            res += f" event[{-i-1}] Total runtime: {total:.1f} ms;"
+            res += f" Exclusive time: {exclusive:.1f} ms; Subcall time: {subcalls:.1f} ms\n"
             if i >= 9: # show last 10 calls
                 break
         return res
 
-    def event(self, ev):
+    def event(self, event):
         """
         Event filter for generating tool tips.
 
-        :param ev: a QEvent instance.
+        :param event: a QEvent instance.
         :return:
         """
-        if ev.type() == QEvent.ToolTip:
+        if event.type() == QEvent.ToolTip:
             for thread, port, y1, y2 in self.portYCoords:
-                if ev.pos().y() >= y1 and ev.pos().y() <= y2:
-                    QToolTip.showText(ev.globalPos(), self.textDescription(thread, port))
+                if event.pos().y() >= y1 and event.pos().y() <= y2:
+                    QToolTip.showText(event.globalPos(), self.textDescription(thread, port))
             return True
-        return super().event(ev)
+        return super().event(event)
 
 class Profiling(ProfilingService):
     """
diff --git a/nexxT/services/gui/PropertyDelegate.py b/nexxT/services/gui/PropertyDelegate.py
index 756391f..497b89f 100644
--- a/nexxT/services/gui/PropertyDelegate.py
+++ b/nexxT/services/gui/PropertyDelegate.py
@@ -8,14 +8,14 @@
 This module provides a delegate for use in the Configuration GUI service to edit properties.
 """
 
-from PySide2.QtCore import Qt
-from PySide2.QtWidgets import QStyledItemDelegate
+from nexxT.Qt.QtCore import Qt
+from nexxT.Qt.QtWidgets import QStyledItemDelegate
 
 class PropertyDelegate(QStyledItemDelegate):
     """
     This class provides a delegate for providing editor widgets for the nexxT gui service Configuration.
     """
-    def __init__(self, model, role, PropertyContent, parent):
+    def __init__(self, model, role, PropertyContent, parent): # pylint: disable=invalid-name
         """
         Constructor.
 
diff --git a/nexxT/services/gui/RecordingControl.py b/nexxT/services/gui/RecordingControl.py
index 6886d8a..7c975d4 100644
--- a/nexxT/services/gui/RecordingControl.py
+++ b/nexxT/services/gui/RecordingControl.py
@@ -10,9 +10,9 @@
 
 import logging
 from pathlib import Path
-from PySide2.QtCore import Qt, QStorageInfo
-from PySide2.QtGui import QIcon, QTextOption
-from PySide2.QtWidgets import QAction, QApplication, QStyle, QWidget, QBoxLayout, QToolBar, QFileDialog
+from nexxT.Qt.QtCore import Qt, QStorageInfo
+from nexxT.Qt.QtGui import QAction, QIcon, QTextOption
+from nexxT.Qt.QtWidgets import QApplication, QStyle, QWidget, QBoxLayout, QToolBar, QFileDialog
 from nexxT.core.Utils import assertMainThread, ElidedLabel
 from nexxT.core.Exceptions import PropertyCollectionPropertyNotFound
 from nexxT.interface import Services
@@ -129,28 +129,28 @@ def _onUpdateStatus(self, _, file, length, bytesWritten):
         if bytesWritten is None:
             bw = "??"
         elif bytesWritten < 1024:
-            bw = "%3d bytes" % bytesWritten
+            bw = f"{bytesWritten:3d} bytes"
         elif bytesWritten < 1024*1024:
-            bw = "%.1f kb" % (bytesWritten/1024)
+            bw = f"{bytesWritten/1024:.1f} kb"
         elif bytesWritten < 1024*1024*1024:
-            bw = "%.1f Mb" % (bytesWritten/1024/1024)
+            bw = f"{bytesWritten/1024/1024:.1f} Mb"
         else:
-            bw = "%.1f Gb" % (bytesWritten/1024/1024/1024)
+            bw = f"{bytesWritten/1024/1024/1024:.1f} Gb"
 
         if length is None:
             sl = "?? s"
         elif length < 60:
-            sl = "%.1f sec" % length
+            sl = f"{length:.1f} sec"
         else:
-            sl = "%.1f min" % (length//60)
+            sl = f"{length/60:.1f} min"
 
         bytesAv = QStorageInfo(file).bytesAvailable()
         if length is not None and bytesWritten is not None and bytesAv >= 0 and bytesWritten > 0:
             timeAv = length*bytesAv/bytesWritten - length
             if timeAv < 60:
-                av = "%.1f sec" % timeAv
+                av = f"{timeAv:.1f} sec"
             elif timeAv < 3600:
-                av = "%.1f min" % (timeAv // 60)
+                av = f"{timeAv/60:.1f} min"
             else:
                 av = "> 1 hour"
         else:
diff --git a/nexxT/shiboken.py b/nexxT/shiboken.py
new file mode 100644
index 0000000..5e87dde
--- /dev/null
+++ b/nexxT/shiboken.py
@@ -0,0 +1,9 @@
+"""
+This module is never imported when executing nexxT. Instead, the Qt module is provided through the QtMetaPackage
+which is preventing python from loading unnecessary extensions. However, it is very handy to have this module, so
+pylint and the IDE auto-completers are happy to know the contents of this module
+"""
+import logging
+from shiboken6 import isValid
+
+logging.getLogger(__name__).warning("nexxT.shiboken imported not via QtMetaPackage!")
diff --git a/nexxT/src/Compatibility.cpp b/nexxT/src/Compatibility.cpp
new file mode 100644
index 0000000..ac02277
--- /dev/null
+++ b/nexxT/src/Compatibility.cpp
@@ -0,0 +1,8 @@
+#include "nexxT/Compatibility.hpp"
+
+using namespace nexxT;
+
+QMenu *Compatibility::getMenuFromAction(QAction *a)
+{
+    return a->menu();
+}
\ No newline at end of file
diff --git a/nexxT/src/DataSamples.cpp b/nexxT/src/DataSamples.cpp
index 00faa4e..48f7fcc 100644
--- a/nexxT/src/DataSamples.cpp
+++ b/nexxT/src/DataSamples.cpp
@@ -5,8 +5,8 @@
  * THE PROGRAM IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND.
  */
 
-#include "DataSamples.hpp"
-#include "Logger.hpp"
+#include "nexxT/DataSamples.hpp"
+#include "nexxT/Logger.hpp"
 #include <chrono>
 #include <atomic>
 
diff --git a/nexxT/src/FilterEnvironment.cpp b/nexxT/src/FilterEnvironment.cpp
index b187be1..3cce3df 100644
--- a/nexxT/src/FilterEnvironment.cpp
+++ b/nexxT/src/FilterEnvironment.cpp
@@ -5,10 +5,10 @@
  * THE PROGRAM IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND.
  */
 
-#include "FilterEnvironment.hpp"
-#include "Filters.hpp"
-#include "Logger.hpp" 
-#include "PropertyCollection.hpp"
+#include "nexxT/FilterEnvironment.hpp"
+#include "nexxT/Filters.hpp"
+#include "nexxT/Logger.hpp" 
+#include "nexxT/PropertyCollection.hpp"
 
 #include <QtCore/QThread>
 #include <QtCore/QCoreApplication>
diff --git a/nexxT/src/Filters.cpp b/nexxT/src/Filters.cpp
index c49da32..6861f6d 100644
--- a/nexxT/src/Filters.cpp
+++ b/nexxT/src/Filters.cpp
@@ -5,12 +5,13 @@
  * THE PROGRAM IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND.
  */
 
-#include "Filters.hpp"
-#include "FilterEnvironment.hpp"
-#include "Ports.hpp"
-#include "InputPortInterface.hpp"
-#include "OutputPortInterface.hpp"
-#include "Logger.hpp"
+#include "nexxT/Filters.hpp"
+#include "nexxT/FilterEnvironment.hpp"
+#include "nexxT/Ports.hpp"
+#include "nexxT/InputPortInterface.hpp"
+#include "nexxT/OutputPortInterface.hpp"
+#include "nexxT/Logger.hpp"
+#include <stdexcept>
 
 using namespace nexxT;
 
diff --git a/nexxT/src/InputPortInterface.cpp b/nexxT/src/InputPortInterface.cpp
index 5b35ab7..be196c2 100644
--- a/nexxT/src/InputPortInterface.cpp
+++ b/nexxT/src/InputPortInterface.cpp
@@ -5,13 +5,13 @@
  * THE PROGRAM IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND.
  */
 
-#include "InputPortInterface.hpp"
-#include "DataSamples.hpp"
-#include "FilterEnvironment.hpp"
-#include "Filters.hpp"
-#include "Logger.hpp"
-#include "Services.hpp"
-#include "Logger.hpp"
+#include "nexxT/InputPortInterface.hpp"
+#include "nexxT/DataSamples.hpp"
+#include "nexxT/FilterEnvironment.hpp"
+#include "nexxT/Filters.hpp"
+#include "nexxT/Logger.hpp"
+#include "nexxT/Services.hpp"
+#include "nexxT/Logger.hpp"
 #include <atomic>
 #include <tuple>
 
@@ -219,10 +219,13 @@ void InputPortInterface::receiveAsync(const QSharedPointer<const DataSample> &sa
             stackDepth--;
         }
         addToQueue(sample);
-        if(!d->interthreadDynamicQueue)
+        if( (!d->interthreadDynamicQueue) || (!semaphore) )
         {
             transmit();
-            semaphore->release(1);
+            if(semaphore)
+            {
+                semaphore->release(1);
+            }
         } else
         {
             if( d->semaphoreN.find(semaphore) == d->semaphoreN.end() )
diff --git a/nexxT/src/Logger.cpp b/nexxT/src/Logger.cpp
index a4690ac..6bb6ec9 100644
--- a/nexxT/src/Logger.cpp
+++ b/nexxT/src/Logger.cpp
@@ -5,7 +5,7 @@
  * THE PROGRAM IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND.
  */
 
-#include "Logger.hpp"
+#include "nexxT/Logger.hpp"
 
 using namespace nexxT;
 
diff --git a/nexxT/src/NexxTPlugins.cpp b/nexxT/src/NexxTPlugins.cpp
index 39d78b3..79a81bc 100644
--- a/nexxT/src/NexxTPlugins.cpp
+++ b/nexxT/src/NexxTPlugins.cpp
@@ -5,9 +5,10 @@
  * THE PROGRAM IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND.
  */
 
-#include "NexxTPlugins.hpp"
-#include "Logger.hpp"
+#include "nexxT/NexxTPlugins.hpp"
+#include "nexxT/Logger.hpp"
 #include <QtCore/QMap>
+#include <stdexcept>
 
 using namespace nexxT;
 
diff --git a/nexxT/src/OutputPortInterface.cpp b/nexxT/src/OutputPortInterface.cpp
index fa48512..78f02d5 100644
--- a/nexxT/src/OutputPortInterface.cpp
+++ b/nexxT/src/OutputPortInterface.cpp
@@ -5,13 +5,13 @@
  * THE PROGRAM IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND.
  */
 
-#include "OutputPortInterface.hpp"
-#include "InputPortInterface.hpp"
-#include "DataSamples.hpp"
-#include "FilterEnvironment.hpp"
-#include "Filters.hpp"
-#include "Logger.hpp"
-#include "Services.hpp"
+#include "nexxT/OutputPortInterface.hpp"
+#include "nexxT/InputPortInterface.hpp"
+#include "nexxT/DataSamples.hpp"
+#include "nexxT/FilterEnvironment.hpp"
+#include "nexxT/Filters.hpp"
+#include "nexxT/Logger.hpp"
+#include "nexxT/Services.hpp"
 #include <atomic>
 
 #include <QtCore/QThread>
@@ -47,9 +47,9 @@ void OutputPortInterface::setupDirectConnection(const SharedPortPtr &op, const S
                      p1, SLOT(receiveSync(const QSharedPointer<const nexxT::DataSample> &)));
 }
 
-QObject *OutputPortInterface::setupInterThreadConnection(const SharedPortPtr &op, const SharedPortPtr &ip, QThread &outputThread)
+QObject *OutputPortInterface::setupInterThreadConnection(const SharedPortPtr &op, const SharedPortPtr &ip, QThread &outputThread, int width)
 {
-    InterThreadConnection *itc = new InterThreadConnection(&outputThread);
+    InterThreadConnection *itc = new InterThreadConnection(&outputThread, width);
     const OutputPortInterface *p0 = dynamic_cast<const OutputPortInterface *>(op.data());
     const InputPortInterface *p1 = dynamic_cast<const InputPortInterface *>(ip.data());
     QObject::connect(p0, SIGNAL(transmitSample(const QSharedPointer<const nexxT::DataSample>&)),
diff --git a/nexxT/src/Ports.cpp b/nexxT/src/Ports.cpp
index 690f529..5bf86d8 100644
--- a/nexxT/src/Ports.cpp
+++ b/nexxT/src/Ports.cpp
@@ -5,14 +5,14 @@
  * THE PROGRAM IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND.
  */
 
-#include "Ports.hpp"
-#include "DataSamples.hpp"
-#include "FilterEnvironment.hpp"
-#include "Filters.hpp"
-#include "Logger.hpp"
-#include "Services.hpp"
-#include "OutputPortInterface.hpp"
-#include "InputPortInterface.hpp"
+#include "nexxT/Ports.hpp"
+#include "nexxT/DataSamples.hpp"
+#include "nexxT/FilterEnvironment.hpp"
+#include "nexxT/Filters.hpp"
+#include "nexxT/Logger.hpp"
+#include "nexxT/Services.hpp"
+#include "nexxT/OutputPortInterface.hpp"
+#include "nexxT/InputPortInterface.hpp"
 #include <atomic>
 
 #include <QtCore/QThread>
@@ -32,9 +32,10 @@ namespace nexxT
 
     struct InterThreadConnectionD
     {
+        int width;
         QSemaphore semaphore;
         std::atomic_bool stopped;
-        InterThreadConnectionD(int n) : semaphore(n), stopped(true) {}
+        InterThreadConnectionD(int width) : width(width), semaphore(width), stopped(true) {}
     };
 
 };
@@ -86,8 +87,8 @@ SharedPortPtr Port::make_shared(Port *port)
     return SharedPortPtr(port);
 }
 
-InterThreadConnection::InterThreadConnection(QThread *from_thread)
-    : d(new InterThreadConnectionD(1))
+InterThreadConnection::InterThreadConnection(QThread *from_thread, int width)
+    : d(new InterThreadConnectionD(width))
 {
     moveToThread(from_thread);
 }
@@ -106,9 +107,9 @@ void InterThreadConnection::receiveSample(const QSharedPointer<const DataSample>
             NEXXT_LOG_WARN("The inter-thread connection is set to stopped mode; data sample discarded.");
             break;
         }
-        if( d->semaphore.tryAcquire(1, 500) )
+        if( (d->width == 0) || (d->semaphore.tryAcquire(1, 500)) )
         {
-            emit transmitInterThread(sample, &d->semaphore);
+            emit transmitInterThread(sample, (d->width > 0) ? (&d->semaphore) : 0 );
             break;
         }
     }
diff --git a/nexxT/src/PropertyCollection.cpp b/nexxT/src/PropertyCollection.cpp
index 1a41506..ef1a5a0 100644
--- a/nexxT/src/PropertyCollection.cpp
+++ b/nexxT/src/PropertyCollection.cpp
@@ -5,7 +5,8 @@
  * THE PROGRAM IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND.
  */
 
-#include "PropertyCollection.hpp"
+#include "nexxT/PropertyCollection.hpp"
+#include <stdexcept>
 
 #include <cstdio>
 
diff --git a/nexxT/src/SConscript.py b/nexxT/src/SConscript.py
index ebf911f..cec936d 100644
--- a/nexxT/src/SConscript.py
+++ b/nexxT/src/SConscript.py
@@ -17,10 +17,24 @@
 include = Dir(sysconfig.get_paths()['include'])
 platinclude = Dir(sysconfig.get_paths()['platinclude'])
 
+if os.environ.get("PYSIDEVERSION", "6") in "52":
+    ver=2
+    standard="c++14"
+elif os.environ.get("PYSIDEVERSION", "6") == "6":
+    ver=6
+    standard="c++17"
+else:
+    raise RuntimeError("invalid env variable PYSIDEVERSION=%s" % os.environ["PYSIDEVERSION"])
+
 env.Append(CPPPATH=[".",
-                    purelib.abspath + "/shiboken2_generator/include",
-                    purelib.abspath + "/PySide2/include/QtCore",
-                    purelib.abspath + "/PySide2/include",
+                    str(srcDir.Dir("..").Dir("include")),
+                    purelib.abspath + "/shiboken%d_generator/include" % ver,
+                    purelib.abspath + "/PySide%d/include/QtCore" % ver,
+                    # TODO: the following 2 lines can be removed after this bug has been
+                    #       fixed: https://bugreports.qt.io/browse/PYSIDE-1627
+                    purelib.abspath + "/PySide%d/include/QtWidgets" % ver,
+                    purelib.abspath + "/PySide%d/include/QtGui" % ver,
+                    purelib.abspath + "/PySide%d/include" % ver,
                     include.abspath,
                     platinclude.abspath,
                     ],
@@ -28,8 +42,8 @@
                     sysconfig.get_paths()['stdlib'],
                     sysconfig.get_paths()['platstdlib'],
                     sysconfig.get_config_vars()['installed_platbase'] + "/libs",
-                    sysconfig.get_paths()['purelib'] + "/shiboken2",
-                    sysconfig.get_paths()['purelib'] + "/PySide2",
+                    sysconfig.get_paths()['purelib'] + "/shiboken%d" % ver,
+                    sysconfig.get_paths()['purelib'] + "/PySide%d" % ver,
                     ]
            )
 
@@ -40,23 +54,35 @@
 else:
     env["SHIBOKEN_INCFLAGS"] = ";".join(env["CPPPATH"])
 
-nexxT_headers = env.RegisterSources(
-    [srcDir.File("NexxTLinkage.hpp"),
-     srcDir.File("DataSamples.hpp"),
-     srcDir.File("Filters.hpp"),
-     ])
-apilib = env.SharedLibrary("nexxT", env.RegisterSources(Split("""
-    DataSamples.cpp 
-    FilterEnvironment.cpp
-    Filters.cpp
-    Logger.cpp
-    Ports.cpp
-    InputPortInterface.cpp
-    OutputPortInterface.cpp
-    Services.cpp
-    PropertyCollection.cpp
-    NexxTPlugins.cpp
-""")), CPPDEFINES=["NEXXT_LIBRARY_COMPILATION"])
+# we have to manually moc because of seperation of headers and sources
+moced = [
+    env.Moc6(os.path.basename(h) + ".cpp", srcDir.File(h))
+        for h in Split("""
+            ../include/nexxT/FilterEnvironment.hpp
+            ../include/nexxT/Filters.hpp
+            ../include/nexxT/InputPortInterface.hpp
+            ../include/nexxT/OutputPortInterface.hpp
+            ../include/nexxT/Ports.hpp
+            ../include/nexxT/Ports.hpp
+            ../include/nexxT/PropertyCollection.hpp
+        """)
+]
+
+apilib = env.SharedLibrary("nexxT", env.RegisterSources(
+    Split("""
+        DataSamples.cpp 
+        FilterEnvironment.cpp
+        Filters.cpp
+        Logger.cpp
+        Ports.cpp
+        InputPortInterface.cpp
+        OutputPortInterface.cpp
+        Services.cpp
+        PropertyCollection.cpp
+        NexxTPlugins.cpp
+        Compatibility.cpp
+    """)) + moced
+    , CPPDEFINES=["NEXXT_LIBRARY_COMPILATION"])
 env.RegisterTargets(apilib)
 
 spath = Dir("./cnexxT-shiboken")
@@ -76,6 +102,7 @@
 targets += [spath.Dir("cnexxT").File("nexxt_basefilterenvironment_wrapper.cpp")]
 targets += [spath.Dir("cnexxT").File("nexxt_plugininterface_wrapper.cpp")]
 targets += [spath.Dir("cnexxT").File("nexxt_logging_wrapper.cpp")]
+targets += [spath.Dir("cnexxT").File("nexxt_compatibility_wrapper.cpp")]
 targets += [spath.Dir("cnexxT").File("qsharedpointer_datasample_wrapper.cpp")]
 targets += [spath.Dir("cnexxT").File("qsharedpointer_filter_wrapper.cpp")]
 targets += [spath.Dir("cnexxT").File("qsharedpointer_port_wrapper.cpp")]
@@ -84,14 +111,17 @@
 
 env = env.Clone()
 env.Append(LIBS=["nexxT"])
-if "linux" in env["target_platform"]:
-    # the : notation is for the linker and enables to use lib names which are not
-    # ending with .so
-    qt5vend = ".".join(env.subst("$QT5VERSION").split(".")[:2])
+if os.environ.get("PYSIDEVERSION", "6") == "6":
+    if "linux" in env["target_platform"]:
+        # the : notation is for the linker and enables to use lib names which are not
+        # ending with .so
+        qt6vend = ".".join(env.subst("$QT6VERSION").split(".")[:2])
 
-    env.Append(LIBS=[":libpyside2.abi3.so." + qt5vend,":libshiboken2.abi3.so." + qt5vend])
+        env.Append(LIBS=[":libpyside6.abi3.so." + qt6vend,":libshiboken6.abi3.so." + qt6vend])
+    else:
+        env.Append(LIBS=["shiboken6.abi3", "pyside6.abi3"])
 else:
-    env.Append(LIBS=["shiboken2.abi3", "pyside2.abi3"])
+    raise RuntimeError("invalid env variable PYSIDEVERSION=%s" % os.environ["PYSIDEVERSION"])
 
 if "manylinux" in env["target_platform"]:
     # we are on a manylinux* platform which doesn't have llvm in required versions
@@ -101,12 +131,14 @@
         source = Dir("#/build/linux_x86_64_release/nexxT/src/cnexxT-shiboken/cnexxT").File(os.path.basename(str(t)))
         dummy.extend(env.InstallAs(t, source))
 else:
+    d = {"ver": ver, "standard": standard}
+    d.update(sysconfig.get_paths())
     dummy = env.Command(targets, env.RegisterSources(Split("cnexxT.h cnexxT.xml")),
                         [
                             Delete("$SPATH"),
-                            sysconfig.get_paths()["scripts"] + "/shiboken2 --generator-set=shiboken --avoid-protected-hack --output-directory=${SPATH} "
-                            "--language-level=c++14 --include-paths=$SHIBOKEN_INCFLAGS --enable-pyside-extensions "
-                            "--typesystem-paths=%(purelib)s/PySide2/typesystems $SOURCES" % sysconfig.get_paths(),
+                            sysconfig.get_paths()["scripts"] + "/shiboken%(ver)d --generator-set=shiboken --avoid-protected-hack --output-directory=${SPATH} "
+                            "--language-level=%(standard)s --include-paths=$SHIBOKEN_INCFLAGS --enable-pyside-extensions "
+                            "--typesystem-paths=%(purelib)s/PySide%(ver)d/typesystems $SOURCES" % d,
                         ], SPATH=spath)
 
 pyext = env.SharedLibrary("cnexxT", dummy,
@@ -119,8 +151,12 @@
 # install python extension and library files into project directory
 env.RegisterTargets(env.Install(srcDir.Dir("..").Dir("binary").Dir(env.subst("$deploy_platform")).Dir(env.subst("$variant")).abspath, pyext+apilib))
 if env["variant"] == "release":
-    env.RegisterTargets(env.Install(srcDir.Dir("..").Dir("include").abspath, Glob(srcDir.abspath + "/*.hpp")))
     qrcsrc = srcDir.File('../../workspace/resources/nexxT.qrc')
-    rccout = env.Qrc5('qrc_resources.py', qrcsrc.abspath, QT5_QRCFLAGS=Split("-g python"))
+    if os.environ.get("PYSIDEVERSION", "6") in "52":
+        rccout = env.Qrc5('qrc_resources.py', qrcsrc.abspath, QT5_QRCFLAGS=Split("-g python"))
+    elif os.environ.get("PYSIDEVERSION", "6") == "6":
+        rccout = env.Qrc6('qrc_resources.py', qrcsrc.abspath, QT6_QRCFLAGS=Split("-g python"))
+    else:
+        raise RuntimeError("invalid env variable PYSIDEVERSION=%s" % os.environ["PYSIDEVERSION"])
     iout = env.Install(srcDir.Dir("..").Dir("core").abspath, rccout)
     env.RegisterTargets(iout)
diff --git a/nexxT/src/Services.cpp b/nexxT/src/Services.cpp
index 4976015..21dc2e8 100644
--- a/nexxT/src/Services.cpp
+++ b/nexxT/src/Services.cpp
@@ -5,17 +5,17 @@
  * THE PROGRAM IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND.
  */
 
-#include "Services.hpp"
-#include "Logger.hpp"
+#include "nexxT/Services.hpp"
+#include "nexxT/Logger.hpp"
 #include <QtCore/QObject>
-#include <QtCore/QMutex>
+#include <QtCore/QRecursiveMutex>
 #include <QtCore/QMap>
 
 using namespace nexxT;
 
 Services *Services::_singleton = 0;
 
-typedef QSharedPointer<QMutex> SharedMutexPtr;
+typedef QSharedPointer<QRecursiveMutex> SharedMutexPtr;
 
 namespace nexxT
 {
@@ -27,7 +27,7 @@ namespace nexxT
 };
 
 Services::Services()
-    : d(new ServicesD{SharedMutexPtr(new QMutex(QMutex::Recursive))})
+    : d(new ServicesD{SharedMutexPtr(new QRecursiveMutex())})
 {
 }
 
diff --git a/nexxT/src/cnexxT.h b/nexxT/src/cnexxT.h
index fddad59..c937261 100644
--- a/nexxT/src/cnexxT.h
+++ b/nexxT/src/cnexxT.h
@@ -7,14 +7,15 @@
 
 #define QT_ANNOTATE_ACCESS_SPECIFIER(a) __attribute__((annotate(#a)))
 
-#include "DataSamples.hpp"
-#include "Ports.hpp"
-#include "OutputPortInterface.hpp"
-#include "InputPortInterface.hpp"
-#include "Filters.hpp"
-#include "FilterEnvironment.hpp"
-#include "Services.hpp"
-#include "PropertyCollection.hpp"
-#include "NexxTPlugins.hpp"
-#include "Logger.hpp"
-#include "SharedPointerTypes.hpp"
+#include "nexxT/DataSamples.hpp"
+#include "nexxT/Ports.hpp"
+#include "nexxT/OutputPortInterface.hpp"
+#include "nexxT/InputPortInterface.hpp"
+#include "nexxT/Filters.hpp"
+#include "nexxT/FilterEnvironment.hpp"
+#include "nexxT/Services.hpp"
+#include "nexxT/PropertyCollection.hpp"
+#include "nexxT/NexxTPlugins.hpp"
+#include "nexxT/Logger.hpp"
+#include "nexxT/SharedPointerTypes.hpp"
+#include "nexxT/Compatibility.hpp"
diff --git a/nexxT/src/cnexxT.xml b/nexxT/src/cnexxT.xml
index 41f9500..a861d1c 100644
--- a/nexxT/src/cnexxT.xml
+++ b/nexxT/src/cnexxT.xml
@@ -19,6 +19,12 @@
     <load-typesystem name="typesystem_core.xml" generate="no" />
     <load-typesystem name="typesystem_core_common.xml" generate="no" />
 
+    <load-typesystem name="typesystem_widgets.xml" generate="no" />
+    <load-typesystem name="typesystem_widgets_common.xml" generate="no" />
+
+    <load-typesystem name="typesystem_gui.xml" generate="no" />
+    <load-typesystem name="typesystem_gui_common.xml" generate="no" />
+
     <primitive-type name="int64_t" />
 
     <smart-pointer-type name="QSharedPointer" type="shared" getter="data" />
@@ -58,7 +64,7 @@
         </object-type>
         
         <object-type name="InputPortInterface" allow-thread="true">
-            <modify-function signature="receiveAsync(const QSharedPointer&lt;const nexxT::DataSample&gt; &amp;, QSemaphore *)">
+            <modify-function signature="receiveAsync(QSharedPointer&lt;const nexxT::DataSample&gt;, QSemaphore *, bool)">
                 <modify-argument index="2">
                     <define-ownership owner="c++"/>
                 </modify-argument>
@@ -134,5 +140,16 @@
 
         <object-type name="Logging" />
 
+        <object-type name="Compatibility">
+            <modify-function signature="getMenuFromAction(QAction*)">
+                <modify-argument index="return">
+                    <define-ownership owner="c++"/>
+                </modify-argument>
+                <modify-argument index="1">
+                    <define-ownership owner="c++"/>
+                </modify-argument>
+            </modify-function>
+        </object-type>
+
     </namespace-type>
 </typesystem>
diff --git a/setup.py b/setup.py
index bd11057..dd3d546 100644
--- a/setup.py
+++ b/setup.py
@@ -22,7 +22,7 @@
 from distutils.command.install import INSTALL_SCHEMES
 
 # remove build results
-for p in ["nexxT/binary", "nexxT/include", "nexxT/tests/binary"]:
+for p in ["nexxT/binary", "nexxT/tests/binary"]:
     if os.path.exists(p):
         shutil.rmtree(p, ignore_errors=True)
     if os.path.exists(p):
@@ -46,7 +46,7 @@ def get_tag(self):
             # uncomment for non-python extensions
             if platform.system() == "Linux":
                 abi = "abi3"
-                plat = "manylinux2014_x86_64"
+                plat = "manylinux_2_28_x86_64"
             else:
                 abi = "none"
             python = "cp37.cp38.cp39"
@@ -73,12 +73,12 @@ def is_pure(*args):
         #print("IS_PURE WAS CALLED!")
         return False
         
-    def get_option_dict(self, k):
-        res = super().get_option_dict(k)
-        if k == "install":
-            res["install_lib"] = "platlib"
-        #print("GET_OPTION_DICT CALLED:", k, res)
-        return res
+    #def get_option_dict(self, k):
+    #    res = super().get_option_dict(k)
+    #    #if k == "install":
+    #    #    res["install_lib"] = "platlib"
+    #    #print("GET_OPTION_DICT CALLED:", k, res)
+    #    return res
     
 if platform.system() == "Linux":
     p = "linux_x86_64"
@@ -122,7 +122,7 @@ def get_option_dict(self, k):
     manifest.write("include LICENSE\n")
     manifest.write("include NOTICE\n")
     if build_required:
-        manifest.write("include nexxT/include/*.hpp\n")
+        manifest.write("include nexxT/include/nexxT/*.hpp\n")
         for bf in build_files:
             manifest.write("include " + bf + "\n")
         manifest.write("exclude nexxT/src/*.*\n")
@@ -133,22 +133,28 @@ def get_option_dict(self, k):
 
 if build_required:
     try:
-        import PySide2
+        if os.environ.get("PYSIDEVERSION", "6") in "6":
+            import PySide6
+        else:
+            raise RuntimeError("Don't know what to do with PYSIDEVERSION=%s" % os.environ.get("PYSIDEVERSION", "6"))
     except ImportError:
-        raise RuntimeError("PySide2 must be installed for building the extension module.")
+        raise RuntimeError("PySide%s must be installed for building the extension module." % os.environ.get("PYSIDEVERSION", "6"))
     cwd = pathlib.Path().absolute()
     os.chdir("workspace")
-    subprocess.run([sys.executable, os.path.dirname(sys.executable) + "/scons", "-j%d" % multiprocessing.cpu_count(), ".."], check=True)
+    if platform.system() == "Linux":
+        subprocess.run([sys.executable, os.path.dirname(sys.executable) + "/scons", "-j%d" % multiprocessing.cpu_count(), ".."], check=True)
+    else:
+        subprocess.run([os.path.join(os.path.dirname(sys.executable), "scons.exe"), "-j%d" % multiprocessing.cpu_count(), ".."], check=True)    
     os.chdir(str(cwd))    
     
 setup(name='nexxT',
       install_requires=[
-        "PySide2==5.15.1", 
-        "shiboken2==5.15.1", 
-        "jsonschema>=3.2.0", 
+        "PySide6==6.4.1",
+        "shiboken6==6.4.1",
+        "jsonschema>=3.2.0",
         "h5py>=2.10.0",
         "setuptools>=41.0.0",
-        'importlib-metadata >= 1.0 ; python_version < "3.8"', 
+        'importlib-metadata >= 1.0 ; python_version < "3.8"',
         "pip-licenses",
       ],
       version=os.environ.get("NEXXT_VERSION", "0.0.0"),
diff --git a/workspace/BUILDING.md b/workspace/BUILDING.md
index 4697cce..c189ed1 100644
--- a/workspace/BUILDING.md
+++ b/workspace/BUILDING.md
@@ -8,14 +8,15 @@ It is assumed that this environment is activated for the following commands.
     
 ## Build dependencies
 The build expects the following dependencies to be available on the system:
-- QT5: set QTDIR for identifying the QT version. Note that the QT installer
+- QT6: set QTDIR for identifying the QT version. Note that the QT installer
   has a known bug of wrong paths in the lib/pkgconfig/*.pc files. These have
-  to be fixed before building.
-- shiboken2_generator: this is referenced in requirements.txt. A binary wheel
+  to be fixed before building. Qt6 seem to have dropped the pkgconfig files.
+  See https://bugreports.qt.io/browse/QTBUG-86080
+- shiboken6_generator: this is referenced in requirements.txt. A binary wheel
   can be found here:
-  https://download.qt.io/official_releases/QtForPython/shiboken2-generator/
+  https://download.qt.io/official_releases/QtForPython/shiboken6-generator/
   This is working for windows and debian builds and probably also for more,
-  even though the PySide2 project doesn't recommend to use the binary 
+  even though the PySide6 project doesn't recommend to use the binary 
   distribution
 - On linux, the binding generator also needs llvm installed and the path can
   be set in the LLVM_INSTALL_DIR variable, e.g. /usr/lib/llvm-7
@@ -24,7 +25,7 @@ The build expects the following dependencies to be available on the system:
 You can use pip to install the required dependencies like that
 
     python -m pip install pip -U
-    pip install -r requirements.txt -f https://download.qt.io/official_releases/QtForPython/shiboken2-generator/ --trusted-host download.qt.io
+    pip install -r requirements.txt -f https://download.qt.io/official_releases/QtForPython/shiboken6-generator/
 
 Be sure to have the environment variables QTDIR and LLVM_INSTALL_DIR set correctly.
 Afterwards you should be able to use 
diff --git a/workspace/SConstruct b/workspace/SConstruct
index d1c8f09..12dd6e7 100644
--- a/workspace/SConstruct
+++ b/workspace/SConstruct
@@ -1,61 +1,95 @@
 import platform
 import os
 import sysconfig
-import PySide2
 
-qtversion = ".".join(PySide2.__version__.split(".")[:2])
+if os.environ.get("PYSIDEVERSION", "6") in "25":
+    import PySide2 as PySide
+    standard = "c++14"
+    pysideversion = "2"
+else:
+    import PySide6 as PySide
+    standard = "c++17"
+    pysideversion = "6"
+
+qtversion = ".".join(PySide.__version__.split(".")[:2])
 
 if platform.system() == "Linux":
-    if os.path.isfile("/etc/centos-release") and "CentOS Linux release 7" in open("/etc/centos-release", "r").read():
-        target_platform = "manylinux2014_x86_64"
+    if os.path.isfile("/etc/manylinux-release"):
+        with open("/etc/manylinux-release", "r") as f:
+            target_platform = f.read().strip()
     else:
         target_platform = "linux_x86_64"
     env = Environment(target_platform=target_platform,
                       deploy_platform="linux_x86_64",
-                      QT5VERSION=qtversion,
                       toolpath=["#/sconstools3"],
                       ENV = os.environ,
                       variant="unknown")
     
-    if "QTDIR" in ARGUMENTS: 
-        env["QT5DIR"] = ARGUMENTS["QTDIR"]
-        os.environ["QTDIR"] = ARGUMENTS["QTDIR"]
-        print("QTDIR=",ARGUMENTS["QTDIR"])
-    env.Tool("qt5")
+    if qtversion[0] == "5":
+        if "QTDIR" in ARGUMENTS: 
+            env["QT5DIR"] = ARGUMENTS["QTDIR"]
+            os.environ["QTDIR"] = ARGUMENTS["QTDIR"]
+            print("QTDIR=",ARGUMENTS["QTDIR"])
+        env["QT5VERSION"] = qtversion
+        env.Tool("qt5")
+        env["QTDIR"] = env["QT5DIR"]
+    elif qtversion[0] == "6":
+        if "QTDIR" in ARGUMENTS:
+            env["QT6DIR"] = ARGUMENTS["QTDIR"]
+            os.environ["QTDIR"] = ARGUMENTS["QTDIR"]
+            print("QTDIR=",ARGUMENTS["QTDIR"])
+        env["QT6VERSION"] = qtversion
+        env.Tool("qt6")
+        env["QTDIR"] = env["QT6DIR"]
+    else:
+        raise RuntimeError("Unknown Qt Version: %s", qtversion)
 
-    env['ENV']['PKG_CONFIG_PATH'] = env.subst("$QT5DIR") + '/lib/pkgconfig'
+    env['ENV']['PKG_CONFIG_PATH'] = env.subst("$QTDIR") + '/lib/pkgconfig'
+    print("pkgconfig path:", env['ENV']['PKG_CONFIG_PATH'])
     # fix shiboken2 lib not found issues
-    env.PrependENVPath('LD_LIBRARY_PATH', sysconfig.get_paths()['purelib'] + "/PySide2/Qt/lib")
+    env.PrependENVPath('LD_LIBRARY_PATH', sysconfig.get_paths()['purelib'] + "/PySide%s/Qt/lib" % pysideversion)
     # make sure moc is working
-    env.PrependENVPath('LD_LIBRARY_PATH', env.subst("$QT5DIR") + "/lib")
+    env.PrependENVPath('LD_LIBRARY_PATH', env.subst("$QTDIR") + "/lib")
     
 else:
     # windows environment
-    env = Environment(MSVC_VERSION="14.0", 
-                      QT5VERSION=qtversion,
-                      tools=["default", "qt5"], 
+    env = Environment(MSVC_VERSION="14.2", 
+                      tools=["default"], 
                       toolpath=["#/sconstools3"],
                       target_platform="msvc_x86_64",
                       deploy_platform="msvc_x86_64",
                       variant="unknown")                     
+    if qtversion[0] == "5":
+        env["QT5VERSION"] = qtversion
+        env.Tool("qt5")
+    elif qtversion[0] == "6":
+        env["QT6VERSION"] = qtversion
+        env.Tool("qt6")
+    else:
+        raise RuntimeError("Unknown Qt Version: %s", qtversion)
 
-env.EnableQt5Modules(['QtCore'])
+# TODO: disable QtWidgets and QtGui after this has been fixed:
+#   https://bugreports.qt.io/browse/PYSIDE-1627
+if qtversion[0] == "5":
+    env.EnableQt5Modules(['QtCore', "QtWidgets", "QtGui"])
+else:
+    env.EnableQt6Modules(["QtCore", "QtWidgets", "QtGui"])
 env.AddMethod(lambda env, args: args, "RegisterSources")
 env.AddMethod(lambda env, args: None, "RegisterTargets")
 env.Append(CPPDEFINES=["Py_LIMITED_API"])
 
 if platform.system() == "Linux":
-    dbg_env = env.Clone(CCFLAGS=Split("-Wall -g -std=c++14 -O0"), #-fvisibility=hidden 
+    dbg_env = env.Clone(CCFLAGS=Split("-Wall -g -std=%s -O0" % standard), #-fvisibility=hidden 
                         LINKFLAGS=Split("-g"),
                         variant="nonopt")
-    rel_env = env.Clone(CCFLAGS=Split("-Wall -std=c++14 -O3"), #-fvisibility=hidden 
+    rel_env = env.Clone(CCFLAGS=Split("-Wall -std=%s -O3" % standard), #-fvisibility=hidden 
                         variant="release")    
 else:    
     env.Append(CPPDEFINES=["_CRT_SECURE_NO_WARNINGS"])
-    dbg_env = env.Clone(CCFLAGS=Split("/nologo /EHsc /TP /W3 /Od /Ob2 /Z7 /MD /std:c++14"),
+    dbg_env = env.Clone(CCFLAGS=Split("/nologo /EHsc /TP /W3 /Od /Ob2 /Z7 /MD /std:%s /Zc:__cplusplus /permissive-" % standard),
                         LINKFLAGS=Split("/nologo /DEBUG"),
                         variant="nonopt")
-    rel_env = env.Clone(CCFLAGS=Split("/nologo /EHsc /TP /W3 /Ox /Z7 /MD /std:c++14"),
+    rel_env = env.Clone(CCFLAGS=Split("/nologo /EHsc /TP /W3 /Ox /Z7 /MD /std:%s /Zc:__cplusplus /permissive-" % standard),
                         LINKFLAGS=Split("/nologo /DEBUG"),
                         variant="release")
 
diff --git a/workspace/pylint.rc b/workspace/pylint.rc
index 7dde677..c634a50 100644
--- a/workspace/pylint.rc
+++ b/workspace/pylint.rc
@@ -1,24 +1,73 @@
-﻿[MASTER]
+[MAIN]
+
+# Analyse import fallback blocks. This can be used to support both Python 2 and
+# 3 compatible code, which means that the block might have code that exists
+# only in one or another interpreter, leading to false positives when analysed.
+analyse-fallback-blocks=no
+
+# Load and enable all available extensions. Use --list-extensions to see a list
+# all available extensions.
+#enable-all-extensions=
+
+# In error mode, messages with a category besides ERROR or FATAL are
+# suppressed, and no reports are done by default. Error mode is compatible with
+# disabling specific errors.
+#errors-only=
+
+# Always return a 0 (non-error) status code, even if lint errors are found.
+# This is primarily useful in continuous integration scripts.
+#exit-zero=
 
 # A comma-separated list of package or module names from where C extensions may
 # be loaded. Extensions are loading into the active Python interpreter and may
 # run arbitrary code.
-extension-pkg-whitelist=PySide2
+extension-pkg-allow-list=
 
-# Add files or directories to the blacklist. They should be base names, not
-# paths.
+# A comma-separated list of package or module names from where C extensions may
+# be loaded. Extensions are loading into the active Python interpreter and may
+# run arbitrary code. (This is an alternative name to extension-pkg-allow-list
+# for backward compatibility.)
+extension-pkg-whitelist=cnexxT,nexxT,nexxT.Qt
+
+# Return non-zero exit code if any of these messages/categories are detected,
+# even if score is above --fail-under value. Syntax same as enable. Messages
+# specified are enabled, while categories only check already-enabled messages.
+fail-on=
+
+# Specify a score threshold under which the program will exit with error.
+fail-under=10
+
+# Interpret the stdin as a python script, whose filename needs to be passed as
+# the module_or_package argument.
+#from-stdin=
+
+# Files or directories to be skipped. They should be base names, not paths.
 ignore=CVS,qrc_resources.py
 
-# Add files or directories matching the regex patterns to the blacklist. The
-# regex matches against base names, not paths.
-ignore-patterns=
+# Add files or directories matching the regular expressions patterns to the
+# ignore-list. The regex matches against paths and can be in Posix or Windows
+# format. Because '\' represents the directory delimiter on Windows systems, it
+# can't be used as an escape character.
+ignore-paths=
+
+# Files or directories matching the regular expression patterns are skipped.
+# The regex matches against base names, not paths. The default value ignores
+# Emacs file locks
+ignore-patterns=^\.#
+
+# List of module names for which member attributes should not be checked
+# (useful for modules/projects where namespaces are manipulated during runtime
+# and thus existing member attributes cannot be deduced by static analysis). It
+# supports qualified module names, as well as Unix pattern matching.
+ignored-modules=cnexxT,nexxT
 
 # Python code to execute, usually for sys.path manipulation such as
 # pygtk.require().
 #init-hook=
 
 # Use multiple processes to speed up Pylint. Specifying 0 will auto-detect the
-# number of processors available to use.
+# number of processors available to use, and will cap the count on Windows to
+# avoid hangs.
 jobs=1
 
 # Control the amount of potential inferred values when inferring a single
@@ -33,8 +82,12 @@ load-plugins=
 # Pickle collected data for later comparisons.
 persistent=yes
 
-# Specify a configuration file.
-#rcfile=
+# Minimum Python version to use for version dependent checks. Will default to
+# the version used to run pylint.
+py-version=3.7
+
+# Discover python modules and packages in the file system subtree.
+recursive=no
 
 # When enabled, pylint would attempt to guess common misconfiguration and emit
 # user-friendly hints instead of false-positive error messages.
@@ -44,101 +97,63 @@ suggestion-mode=yes
 # active Python interpreter and may run arbitrary code.
 unsafe-load-any-extension=no
 
+# In verbose mode, extra non-checker-related info will be displayed.
+#verbose=
+
+
+[REPORTS]
+
+# Python expression which should return a score less than or equal to 10. You
+# have access to the variables 'fatal', 'error', 'warning', 'refactor',
+# 'convention', and 'info' which contain the number of messages in each
+# category, as well as 'statement' which is the total number of statements
+# analyzed. This score is used by the global evaluation report (RP0004).
+evaluation=max(0, 0 if fatal else 10.0 - ((float(5 * error + warning + refactor + convention) / statement) * 10))
+
+# Template used to display messages. This is a python new-style format string
+# used to format the message information. See doc for all details.
+msg-template=
+
+# Set the output format. Available formats are text, parseable, colorized, json
+# and msvs (visual studio). You can also give a reporter class, e.g.
+# mypackage.mymodule.MyReporterClass.
+#output-format=
+
+# Tells whether to display a full report or only the messages.
+reports=no
+
+# Activate the evaluation score.
+score=yes
+
 
 [MESSAGES CONTROL]
 
 # Only show warnings with the listed confidence levels. Leave empty to show
-# all. Valid levels: HIGH, INFERENCE, INFERENCE_FAILURE, UNDEFINED.
-confidence=
+# all. Valid levels: HIGH, CONTROL_FLOW, INFERENCE, INFERENCE_FAILURE,
+# UNDEFINED.
+confidence=HIGH,
+           CONTROL_FLOW,
+           INFERENCE,
+           INFERENCE_FAILURE,
+           UNDEFINED
 
 # Disable the message, report, category or checker with the given id(s). You
 # can either give multiple identifiers separated by comma (,) or put this
 # option multiple times (only on the command line, not in the configuration
 # file where it should appear only once). You can also use "--disable=all" to
-# disable everything first and then reenable specific checks. For example, if
+# disable everything first and then re-enable specific checks. For example, if
 # you want to run only the similarities checker, you can use "--disable=all
 # --enable=similarities". If you want to run only the classes checker, but have
 # no Warning level messages displayed, use "--disable=all --enable=classes
 # --disable=W".
-disable=print-statement,
-        parameter-unpacking,
-        unpacking-in-except,
-        old-raise-syntax,
-        backtick,
-        long-suffix,
-        old-ne-operator,
-        old-octal-literal,
-        import-star-module-level,
-        non-ascii-bytes-literal,
-        raw-checker-failed,
+disable=raw-checker-failed,
         bad-inline-option,
         locally-disabled,
         file-ignored,
         suppressed-message,
         useless-suppression,
         deprecated-pragma,
-        use-symbolic-message-instead,
-        apply-builtin,
-        basestring-builtin,
-        buffer-builtin,
-        cmp-builtin,
-        coerce-builtin,
-        execfile-builtin,
-        file-builtin,
-        long-builtin,
-        raw_input-builtin,
-        reduce-builtin,
-        standarderror-builtin,
-        unicode-builtin,
-        xrange-builtin,
-        coerce-method,
-        delslice-method,
-        getslice-method,
-        setslice-method,
-        no-absolute-import,
-        old-division,
-        dict-iter-method,
-        dict-view-method,
-        next-method-called,
-        metaclass-assignment,
-        indexing-exception,
-        raising-string,
-        reload-builtin,
-        oct-method,
-        hex-method,
-        nonzero-method,
-        cmp-method,
-        input-builtin,
-        round-builtin,
-        intern-builtin,
-        unichr-builtin,
-        map-builtin-not-iterating,
-        zip-builtin-not-iterating,
-        range-builtin-not-iterating,
-        filter-builtin-not-iterating,
-        using-cmp-argument,
-        eq-without-hash,
-        div-method,
-        idiv-method,
-        rdiv-method,
-        exception-message-attribute,
-        invalid-str-codec,
-        sys-max-int,
-        bad-python3-import,
-        deprecated-string-function,
-        deprecated-str-translate-call,
-        deprecated-itertools-function,
-        deprecated-types-field,
-        next-method-defined,
-        dict-items-not-iterating,
-        dict-keys-not-iterating,
-        dict-values-not-iterating,
-        deprecated-operator-function,
-        deprecated-urllib-function,
-        xreadlines-attribute,
-        deprecated-sys-function,
-        exception-escape,
-        comprehension-escape
+        use-symbolic-message-instead
 
 # Enable the message, report, category or checker with the given id(s). You can
 # either give multiple identifier separated by comma (,) or put this option
@@ -147,29 +162,163 @@ disable=print-statement,
 enable=c-extension-no-member
 
 
-[REPORTS]
+[FORMAT]
 
-# Python expression which should return a score less than or equal to 10. You
-# have access to the variables 'error', 'warning', 'refactor', and 'convention'
-# which contain the number of messages in each category, as well as 'statement'
-# which is the total number of statements analyzed. This score is used by the
-# global evaluation report (RP0004).
-evaluation=10.0 - ((float(5 * error + warning + refactor + convention) / statement) * 10)
+# Expected format of line ending, e.g. empty (any line ending), LF or CRLF.
+expected-line-ending-format=
 
-# Template used to display messages. This is a python new-style format string
-# used to format the message information. See doc for all details.
-#msg-template=
+# Regexp for a line that is allowed to be longer than the limit.
+ignore-long-lines=^\s*(# )?<?https?://\S+>?$
 
-# Set the output format. Available formats are text, parseable, colorized, json
-# and msvs (visual studio). You can also give a reporter class, e.g.
-# mypackage.mymodule.MyReporterClass.
-output-format=text
+# Number of spaces of indent required inside a hanging or continued line.
+indent-after-paren=4
 
-# Tells whether to display a full report or only the messages.
-reports=no
+# String used as indentation unit. This is usually "    " (4 spaces) or "\t" (1
+# tab).
+indent-string='    '
 
-# Activate the evaluation score.
-score=yes
+# Maximum number of characters on a single line.
+max-line-length=120
+
+# Maximum number of lines in a module.
+max-module-lines=1000
+
+# Allow the body of a class to be on the same line as the declaration if body
+# contains single statement.
+single-line-class-stmt=no
+
+# Allow the body of an if to be on the same line as the test if there is no
+# else.
+single-line-if-stmt=no
+
+
+[TYPECHECK]
+
+# List of decorators that produce context managers, such as
+# contextlib.contextmanager. Add to this list to register other decorators that
+# produce valid context managers.
+contextmanager-decorators=contextlib.contextmanager
+
+# List of members which are set dynamically and missed by pylint inference
+# system, and so shouldn't trigger E1101 when accessed. Python regular
+# expressions are accepted.
+generated-members=
+
+# Tells whether to warn about missing members when the owner of the attribute
+# is inferred to be None.
+ignore-none=yes
+
+# This flag controls whether pylint should warn about no-member and similar
+# checks whenever an opaque object is returned when inferring. The inference
+# can return multiple potential results while evaluating a Python object, but
+# some branches might not be evaluated, which results in partial inference. In
+# that case, it might be useful to still emit no-member and other checks for
+# the rest of the inferred objects.
+ignore-on-opaque-inference=yes
+
+# List of symbolic message names to ignore for Mixin members.
+ignored-checks-for-mixins=no-member,
+                          not-async-context-manager,
+                          not-context-manager,
+                          attribute-defined-outside-init
+
+# List of class names for which member attributes should not be checked (useful
+# for classes with dynamically set attributes). This supports the use of
+# qualified names.
+ignored-classes=optparse.Values,thread._local,_thread._local,argparse.Namespace
+
+# Show a hint with possible names when a member name was not found. The aspect
+# of finding the hint is based on edit distance.
+missing-member-hint=yes
+
+# The minimum edit distance a name should have in order to be considered a
+# similar match for a missing member name.
+missing-member-hint-distance=1
+
+# The total number of similar names that should be taken in consideration when
+# showing a hint for a missing member.
+missing-member-max-choices=1
+
+# Regex pattern to define which classes are considered mixins.
+mixin-class-rgx=.*[Mm]ixin
+
+# List of decorators that change the signature of a decorated function.
+signature-mutators=
+
+
+[SPELLING]
+
+# Limits count of emitted suggestions for spelling mistakes.
+max-spelling-suggestions=4
+
+# Spelling dictionary name. Available dictionaries: none. To make it work,
+# install the 'python-enchant' package.
+spelling-dict=
+
+# List of comma separated words that should be considered directives if they
+# appear at the beginning of a comment and should not be checked.
+spelling-ignore-comment-directives=fmt: on,fmt: off,noqa:,noqa,nosec,isort:skip,mypy:
+
+# List of comma separated words that should not be checked.
+spelling-ignore-words=
+
+# A path to a file that contains the private dictionary; one word per line.
+spelling-private-dict-file=
+
+# Tells whether to store unknown words to the private dictionary (see the
+# --spelling-private-dict-file option) instead of raising a message.
+spelling-store-unknown-words=no
+
+
+[EXCEPTIONS]
+
+# Exceptions that will emit a warning when caught.
+overgeneral-exceptions=BaseException,
+                       Exception
+
+
+[METHOD_ARGS]
+
+# List of qualified names (i.e., library.method) which require a timeout
+# parameter e.g. 'requests.api.get,requests.api.post'
+timeout-methods=requests.api.delete,requests.api.get,requests.api.head,requests.api.options,requests.api.patch,requests.api.post,requests.api.put,requests.api.request
+
+
+[CLASSES]
+
+# Warn about protected attribute access inside special methods
+check-protected-access-in-special-methods=no
+
+# List of method names used to declare (i.e. assign) instance attributes.
+defining-attr-methods=__init__,
+                      __new__,
+                      setUp,
+                      __post_init__
+
+# List of member names, which should be excluded from the protected access
+# warning.
+exclude-protected=_asdict,
+                  _fields,
+                  _replace,
+                  _source,
+                  _make
+
+# List of valid names for the first argument in a class method.
+valid-classmethod-first-arg=cls
+
+# List of valid names for the first argument in a metaclass class method.
+valid-metaclass-classmethod-first-arg=cls
+
+
+[STRING]
+
+# This flag controls whether inconsistent-quotes generates a warning when the
+# character used as a quote delimiter is used inconsistently within a module.
+check-quote-consistency=no
+
+# This flag controls whether the implicit-str-concat should generate a warning
+# on implicit string concatenation in sequences defined over several lines.
+check-str-concat-over-line-jumps=no
 
 
 [REFACTORING]
@@ -181,7 +330,65 @@ max-nested-blocks=5
 # inconsistent-return-statements if a never returning function is called then
 # it will be considered as an explicit return statement and no message will be
 # printed.
-never-returning-functions=sys.exit
+never-returning-functions=sys.exit,argparse.parse_error
+
+
+[IMPORTS]
+
+# List of modules that can be imported at any level, not just the top level
+# one.
+allow-any-import-level=
+
+# Allow wildcard imports from modules that define __all__.
+allow-wildcard-with-all=no
+
+# Deprecated modules which should not be used, separated by a comma.
+deprecated-modules=
+
+# Output a graph (.gv or any supported image format) of external dependencies
+# to the given file (report RP0402 must not be disabled).
+ext-import-graph=
+
+# Output a graph (.gv or any supported image format) of all (i.e. internal and
+# external) dependencies to the given file (report RP0402 must not be
+# disabled).
+import-graph=
+
+# Output a graph (.gv or any supported image format) of internal dependencies
+# to the given file (report RP0402 must not be disabled).
+int-import-graph=
+
+# Force import order to recognize a module as part of the standard
+# compatibility libraries.
+known-standard-library=
+
+# Force import order to recognize a module as part of a third party library.
+known-third-party=enchant
+
+# Couples of modules and preferred modules, separated by a comma.
+preferred-modules=
+
+
+[MISCELLANEOUS]
+
+# List of note tags to take in consideration, separated by a comma.
+notes=FIXME,
+      XXX,
+      TODO
+
+# Regular expression of note tags to take in consideration.
+notes-rgx=
+
+
+[LOGGING]
+
+# The type of string formatting that logging methods do. `old` means using %
+# formatting, `new` is for `{}` formatting.
+logging-format-style=old
+
+# Logging modules to check that the string format arguments are in logging
+# function parameter format.
+logging-modules=logging
 
 
 [BASIC]
@@ -190,13 +397,15 @@ never-returning-functions=sys.exit
 argument-naming-style=camelCase
 
 # Regular expression matching correct argument names. Overrides argument-
-# naming-style.
+# naming-style. If left empty, argument names will be checked with the set
+# naming style.
 #argument-rgx=
 
 # Naming style matching correct attribute names.
 attr-naming-style=camelCase
 
 # Regular expression matching correct attribute names. Overrides attr-naming-
+# style. If left empty, attribute names will be checked with the set naming
 # style.
 #attr-rgx=
 
@@ -208,24 +417,38 @@ bad-names=foo,
           tutu,
           tata
 
+# Bad variable names regexes, separated by a comma. If names match any regex,
+# they will always be refused
+bad-names-rgxs=
+
 # Naming style matching correct class attribute names.
 class-attribute-naming-style=any
 
 # Regular expression matching correct class attribute names. Overrides class-
-# attribute-naming-style.
+# attribute-naming-style. If left empty, class attribute names will be checked
+# with the set naming style.
 #class-attribute-rgx=
 
+# Naming style matching correct class constant names.
+class-const-naming-style=UPPER_CASE
+
+# Regular expression matching correct class constant names. Overrides class-
+# const-naming-style. If left empty, class constant names will be checked with
+# the set naming style.
+#class-const-rgx=
+
 # Naming style matching correct class names.
 class-naming-style=PascalCase
 
 # Regular expression matching correct class names. Overrides class-naming-
-# style.
+# style. If left empty, class names will be checked with the set naming style.
 #class-rgx=
 
 # Naming style matching correct constant names.
 #const-naming-style=UPPER_CASE
 
 # Regular expression matching correct constant names. Overrides const-naming-
+# style. If left empty, constant names will be checked with the set naming
 # style.
 const-rgx=(logger)|([A-Z][A-Z_0-9]*)
 
@@ -237,7 +460,8 @@ docstring-min-length=-1
 function-naming-style=camelCase
 
 # Regular expression matching correct function names. Overrides function-
-# naming-style.
+# naming-style. If left empty, function names will be checked with the set
+# naming style.
 #function-rgx=
 
 # Good variable names which should always be accepted, separated by a comma.
@@ -248,6 +472,10 @@ good-names=i,
            Run,
            _
 
+# Good variable names regexes, separated by a comma. If names match any regex,
+# they will always be accepted
+good-names-rgxs=
+
 # Include a hint for the correct naming format with invalid-name.
 include-naming-hint=no
 
@@ -255,22 +483,23 @@ include-naming-hint=no
 inlinevar-naming-style=any
 
 # Regular expression matching correct inline iteration names. Overrides
-# inlinevar-naming-style.
+# inlinevar-naming-style. If left empty, inline iteration names will be checked
+# with the set naming style.
 #inlinevar-rgx=
 
 # Naming style matching correct method names.
 method-naming-style=camelCase
 
 # Regular expression matching correct method names. Overrides method-naming-
-# style.
+# style. If left empty, method names will be checked with the set naming style.
 #method-rgx=
 
 # Naming style matching correct module names.
 module-naming-style=PascalCase
 
 # Regular expression matching correct module names. Overrides module-naming-
-# style.
-module-rgx=([A-Z_][a-zA-Z0-9]+)|(core)|(interface)|(gui)|(services)|(filters)|(hdf5)|(examples)|(framework)$
+# style. If left empty, module names will be checked with the set naming style.
+module-rgx=([A-Z_][a-zA-Z0-9]+)|(nexxT)|(core)|(interface)|(gui)|(services)|(filters)|(hdf5)|(examples)|(framework)$
 
 # Colon-delimited sets of names that determine each other's naming style when
 # the name regexes allow several styles.
@@ -285,221 +514,46 @@ no-docstring-rgx=^_
 # These decorators are taken in consideration only for invalid-name.
 property-classes=abc.abstractproperty
 
+# Regular expression matching correct type variable names. If left empty, type
+# variable names will be checked with the set naming style.
+#typevar-rgx=
+
 # Naming style matching correct variable names.
 variable-naming-style=camelCase
 
 # Regular expression matching correct variable names. Overrides variable-
-# naming-style.
+# naming-style. If left empty, variable names will be checked with the set
+# naming style.
 variable-rgx=[a-z_][a-zA-Z0-9_]{0,30}$
 
-[FORMAT]
-
-# Expected format of line ending, e.g. empty (any line ending), LF or CRLF.
-expected-line-ending-format=
-
-# Regexp for a line that is allowed to be longer than the limit.
-ignore-long-lines=^\s*(# )?<?https?://\S+>?$
-
-# Number of spaces of indent required inside a hanging or continued line.
-indent-after-paren=4
-
-# String used as indentation unit. This is usually "    " (4 spaces) or "\t" (1
-# tab).
-indent-string='    '
-
-# Maximum number of characters on a single line.
-max-line-length=120
-
-# Maximum number of lines in a module.
-max-module-lines=1000
-
-# List of optional constructs for which whitespace checking is disabled. `dict-
-# separator` is used to allow tabulation in dicts, etc.: {1  : 1,\n222: 2}.
-# `trailing-comma` allows a space between comma and closing bracket: (a, ).
-# `empty-line` allows space-only lines.
-no-space-check=trailing-comma,
-               dict-separator
-
-# Allow the body of a class to be on the same line as the declaration if body
-# contains single statement.
-single-line-class-stmt=no
-
-# Allow the body of an if to be on the same line as the test if there is no
-# else.
-single-line-if-stmt=no
-
-
-[LOGGING]
-
-# Format style used to check logging format string. `old` means using %
-# formatting, `new` is for `{}` formatting,and `fstr` is for f-strings.
-logging-format-style=old
-
-# Logging modules to check that the string format arguments are in logging
-# function parameter format.
-logging-modules=logging
-
-
-[MISCELLANEOUS]
-
-# List of note tags to take in consideration, separated by a comma.
-notes=FIXME,
-      XXX,
-      TODO
-
 
 [SIMILARITIES]
 
-# Ignore comments when computing similarities.
+# Comments are removed from the similarity computation
 ignore-comments=yes
 
-# Ignore docstrings when computing similarities.
+# Docstrings are removed from the similarity computation
 ignore-docstrings=yes
 
-# Ignore imports when computing similarities.
-ignore-imports=no
-
-# Minimum lines number of a similarity.
-min-similarity-lines=8
-
-
-[SPELLING]
-
-# Limits count of emitted suggestions for spelling mistakes.
-max-spelling-suggestions=4
+# Imports are removed from the similarity computation
+ignore-imports=yes
 
-# Spelling dictionary name. Available dictionaries: none. To make it work,
-# install the python-enchant package.
-spelling-dict=
-
-# List of comma separated words that should not be checked.
-spelling-ignore-words=
-
-# A path to a file that contains the private dictionary; one word per line.
-spelling-private-dict-file=
-
-# Tells whether to store unknown words to the private dictionary (see the
-# --spelling-private-dict-file option) instead of raising a message.
-spelling-store-unknown-words=no
-
-
-[STRING]
-
-# This flag controls whether the implicit-str-concat-in-sequence should
-# generate a warning on implicit string concatenation in sequences defined over
-# several lines.
-check-str-concat-over-line-jumps=no
-
-
-[TYPECHECK]
-
-# List of decorators that produce context managers, such as
-# contextlib.contextmanager. Add to this list to register other decorators that
-# produce valid context managers.
-contextmanager-decorators=contextlib.contextmanager
+# Signatures are removed from the similarity computation
+ignore-signatures=yes
 
-# List of members which are set dynamically and missed by pylint inference
-# system, and so shouldn't trigger E1101 when accessed. Python regular
-# expressions are accepted.
-generated-members=
-
-# Tells whether missing members accessed in mixin class should be ignored. A
-# mixin class is detected if its name ends with "mixin" (case insensitive).
-ignore-mixin-members=yes
-
-# Tells whether to warn about missing members when the owner of the attribute
-# is inferred to be None.
-ignore-none=yes
-
-# This flag controls whether pylint should warn about no-member and similar
-# checks whenever an opaque object is returned when inferring. The inference
-# can return multiple potential results while evaluating a Python object, but
-# some branches might not be evaluated, which results in partial inference. In
-# that case, it might be useful to still emit no-member and other checks for
-# the rest of the inferred objects.
-ignore-on-opaque-inference=yes
-
-# List of class names for which member attributes should not be checked (useful
-# for classes with dynamically set attributes). This supports the use of
-# qualified names.
-ignored-classes=optparse.Values,thread._local,_thread._local
-
-# List of module names for which member attributes should not be checked
-# (useful for modules/projects where namespaces are manipulated during runtime
-# and thus existing member attributes cannot be deduced by static analysis). It
-# supports qualified module names, as well as Unix pattern matching.
-ignored-modules=cnexxT
-
-# Show a hint with possible names when a member name was not found. The aspect
-# of finding the hint is based on edit distance.
-missing-member-hint=yes
-
-# The minimum edit distance a name should have in order to be considered a
-# similar match for a missing member name.
-missing-member-hint-distance=1
-
-# The total number of similar names that should be taken in consideration when
-# showing a hint for a missing member.
-missing-member-max-choices=1
-
-# List of decorators that change the signature of a decorated function.
-signature-mutators=
-
-
-[VARIABLES]
-
-# List of additional names supposed to be defined in builtins. Remember that
-# you should avoid defining new builtins when possible.
-additional-builtins=
-
-# Tells whether unused global variables should be treated as a violation.
-allow-global-unused-variables=yes
-
-# List of strings which can identify a callback function by name. A callback
-# name must start or end with one of those strings.
-callbacks=cb_,
-          _cb
-
-# A regular expression matching the name of dummy variables (i.e. expected to
-# not be used).
-dummy-variables-rgx=_+$|(_[a-zA-Z0-9_]*[a-zA-Z0-9]+?$)|dummy|^ignored_|^unused_
-
-# Argument names that match this expression will be ignored. Default to name
-# with leading underscore.
-ignored-argument-names=_.*|^ignored_|^unused_
-
-# Tells whether we should check for unused import in __init__ files.
-init-import=no
-
-# List of qualified module names which can have objects that can redefine
-# builtins.
-redefining-builtins-modules=six.moves,past.builtins,future.builtins,builtins,io
-
-
-[CLASSES]
-
-# List of method names used to declare (i.e. assign) instance attributes.
-defining-attr-methods=__init__,
-                      __new__,
-                      setUp,
-                      __post_init__
-
-# List of member names, which should be excluded from the protected access
-# warning.
-exclude-protected=_asdict,
-                  _fields,
-                  _replace,
-                  _source,
-                  _make
+# Minimum lines number of a similarity.
+min-similarity-lines=7
 
-# List of valid names for the first argument in a class method.
-valid-classmethod-first-arg=cls
 
-# List of valid names for the first argument in a metaclass class method.
-valid-metaclass-classmethod-first-arg=cls
+[DESIGN]
 
+# List of regular expressions of class ancestor names to ignore when counting
+# public methods (see R0903)
+exclude-too-few-public-methods=
 
-[DESIGN]
+# List of qualified class names to ignore when counting class parents (see
+# R0901)
+ignored-parents=
 
 # Maximum number of arguments for function / method.
 # CW: increased from 5 to 8
@@ -534,49 +588,33 @@ max-statements=50
 min-public-methods=0 
 
 
-[IMPORTS]
-
-# List of modules that can be imported at any level, not just the top level
-# one.
-allow-any-import-level=
-
-# Allow wildcard imports from modules that define __all__.
-allow-wildcard-with-all=no
-
-# Analyse import fallback blocks. This can be used to support both Python 2 and
-# 3 compatible code, which means that the block might have code that exists
-# only in one or another interpreter, leading to false positives when analysed.
-analyse-fallback-blocks=no
-
-# Deprecated modules which should not be used, separated by a comma.
-deprecated-modules=optparse,tkinter.tix
-
-# Create a graph of external dependencies in the given file (report RP0402 must
-# not be disabled).
-ext-import-graph=
+[VARIABLES]
 
-# Create a graph of every (i.e. internal and external) dependencies in the
-# given file (report RP0402 must not be disabled).
-import-graph=
+# List of additional names supposed to be defined in builtins. Remember that
+# you should avoid defining new builtins when possible.
+additional-builtins=
 
-# Create a graph of internal dependencies in the given file (report RP0402 must
-# not be disabled).
-int-import-graph=
+# Tells whether unused global variables should be treated as a violation.
+allow-global-unused-variables=yes
 
-# Force import order to recognize a module as part of the standard
-# compatibility libraries.
-known-standard-library=
+# List of names allowed to shadow builtins
+allowed-redefined-builtins=
 
-# Force import order to recognize a module as part of a third party library.
-known-third-party=enchant
+# List of strings which can identify a callback function by name. A callback
+# name must start or end with one of those strings.
+callbacks=cb_,
+          _cb
 
-# Couples of modules and preferred modules, separated by a comma.
-preferred-modules=
+# A regular expression matching the name of dummy variables (i.e. expected to
+# not be used).
+dummy-variables-rgx=_+$|(_[a-zA-Z0-9_]*[a-zA-Z0-9]+?$)|dummy|^ignored_|^unused_
 
+# Argument names that match this expression will be ignored.
+ignored-argument-names=_.*|^ignored_|^unused_
 
-[EXCEPTIONS]
+# Tells whether we should check for unused import in __init__ files.
+init-import=no
 
-# Exceptions that will emit a warning when being caught. Defaults to
-# "BaseException, Exception".
-overgeneral-exceptions=BaseException,
-                       Exception
+# List of qualified module names which can have objects that can redefine
+# builtins.
+redefining-builtins-modules=six.moves,past.builtins,future.builtins,builtins,io
diff --git a/workspace/requirements.txt b/workspace/requirements.txt
index 17faf7f..2ec32d9 100644
--- a/workspace/requirements.txt
+++ b/workspace/requirements.txt
@@ -1,12 +1,12 @@
-PySide2==5.15.1
-scons==3.1.2
-shiboken2==5.15.1
-shiboken2-generator==5.15.1
-pytest==6.1.2
-pytest-cov==2.10.1
-pylint==2.4.4
-pytest-qt==3.3.0
+PySide6==6.4.1
+scons==4.3.0
+shiboken6==6.4.1
+shiboken6-generator==6.4.1
+pytest==7.1.3
+pytest-cov==4.0.0
+pylint==2.15.4
+pytest-qt==4.1.0
 pytest-xvfb==2.0.0
-pytest-xdist==2.1.0
-pytest-timeout==2.0.1
+pytest-xdist==2.5.0
+pytest-timeout==2.1.0
 -e ..
diff --git a/workspace/sconstools3/qt6/README.rst b/workspace/sconstools3/qt6/README.rst
new file mode 100644
index 0000000..b9f8e27
--- /dev/null
+++ b/workspace/sconstools3/qt6/README.rst
@@ -0,0 +1,351 @@
+####################################
+The SCons qt5 tool
+####################################
+
+Basics
+======
+This tool can be used to compile Qt projects, designed for versions 5.x.y and higher.
+It is not usable for Qt3 and older versions, since some of the helper tools
+(``moc``, ``uic``) behave different.
+
+Install
+-------
+Installing it, requires you to copy (or, even better: checkout) the contents of the
+package's ``qt5`` folder to
+
+#. "``/path_to_your_project/site_scons/site_tools/qt5``", if you need the Qt5 Tool in one project only, or
+#. "``~/.scons/site_scons/site_tools/qt5``", for a system-wide installation under your current login.
+
+For more infos about this, please refer to 
+
+* the SCons User's Guide, sect. "Where to put your custom Builders and Tools" and
+* the SCons Tools Wiki page at `http://scons.org/wiki/ToolsIndex <http://scons.org/wiki/ToolsIndex/>`_.
+
+How to activate
+---------------
+For activating the tool "qt5", you have to add its name to the Environment constructor,
+like this
+
+::
+
+    env = Environment(tools=['default','qt5'])
+
+
+On its startup, the Qt5 tool tries to read the variable ``QT5DIR`` from the current
+Environment and ``os.environ``. If it is not set, the value of ``QTDIR`` (in
+Environment/``os.environ``) is used as a fallback.
+
+So, you either have to explicitly give the path of your Qt5 installation to the
+Environment with
+
+::
+
+    env['QT5DIR'] = '/usr/local/Trolltech/Qt-5.2.3'
+
+
+or set the ``QT5DIR`` as environment variable in your shell.
+
+
+Requirements
+------------
+Under Linux, "qt5" uses the system tool ``pkg-config`` for automatically
+setting the required compile and link flags of the single Qt5 modules (like QtCore,
+QtGui,...).
+This means that
+
+#. you should have ``pkg-config`` installed, and
+#. you additionally have to set ``PKG_CONFIG_PATH`` in your shell environment, such
+   that it points to $``QT5DIR/lib/pkgconfig`` (or $``QT5DIR/lib`` for some older versions).
+
+Based on these two environment variables (``QT5DIR`` and ``PKG_CONFIG_PATH``),
+the "qt5" tool initializes all ``QT5_*``
+construction variables listed in the Reference manual. This happens when the tool
+is "detected" during Environment construction. As a consequence, the setup
+of the tool gets a two-stage process, if you want to override the values provided
+by your current shell settings:
+
+::
+
+    # Stage 1: create plain environment
+    qtEnv = Environment()
+    # Set new vars
+    qtEnv['QT5DIR'] = '/usr/local/Trolltech/Qt-5.2.3
+    qtEnv['ENV']['PKG_CONFIG_PATH'] = '/usr/local/Trolltech/Qt-5.2.3/lib/pkgconfig'
+    # Stage 2: add qt5 tool
+    qtEnv.Tool('qt5')
+
+
+
+
+Suggested boilerplate
+=====================
+Based on the requirements above, we suggest a simple ready-to-go setup
+as follows:
+
+SConstruct
+
+::
+
+    # Detect Qt version
+    qtdir = detectLatestQtDir()
+
+    # Create base environment
+    baseEnv = Environment()
+    #...further customization of base env
+
+    # Clone Qt environment
+    qtEnv = baseEnv.Clone()
+    # Set QT5DIR and PKG_CONFIG_PATH
+    qtEnv['ENV']['PKG_CONFIG_PATH'] = os.path.join(qtdir, 'lib/pkgconfig')
+    qtEnv['QT5DIR'] = qtdir
+    # Add qt5 tool
+    qtEnv.Tool('qt5')
+    #...further customization of qt env
+
+    # Export environments
+    Export('baseEnv qtEnv')
+
+    # Your other stuff...
+    # ...including the call to your SConscripts
+
+
+In a SConscript
+
+::
+
+    # Get the Qt5 environment
+    Import('qtEnv')
+    # Clone it
+    env = qtEnv.clone()
+    # Patch it
+    env.Append(CCFLAGS=['-m32']) # or whatever
+    # Use it
+    env.StaticLibrary('foo', Glob('*.cpp'))
+
+
+The detection of the Qt directory could be as simple as directly assigning
+a fixed path
+
+::
+
+    def detectLatestQtDir():
+        return "/usr/local/qt5.3.2"
+
+
+or a little more sophisticated
+
+::
+
+    # Tries to detect the path to the installation of Qt with
+    # the highest version number
+    def detectLatestQtDir():
+        if sys.platform.startswith("linux"):
+            # Simple check: inspect only '/usr/local/Trolltech'
+            paths = glob.glob('/usr/local/Trolltech/*')
+            if len(paths):
+                paths.sort()
+                return paths[-1]
+            else:
+                return ""
+        else:
+            # Simple check: inspect only 'C:\Qt'
+            paths = glob.glob('C:\\Qt\\*')
+            if len(paths):
+                paths.sort()
+                return paths[-1]
+            else:
+                return os.environ.get("QTDIR","")
+
+
+
+A first project
+===============
+The following SConscript is for a simple project with
+some cxx files, using the QtCore, QtGui
+and QtNetwork modules:
+
+::
+
+    Import('qtEnv')
+    env = qtEnv.Clone()
+    env.EnableQt5Modules([
+                          'QtGui',
+                          'QtCore',
+                          'QtNetwork'
+                         ])
+    # Add your CCFLAGS and CPPPATHs to env here...
+
+    env.Program('foo', Glob('*.cpp')) 
+
+
+
+MOC it up
+=========
+For the basic support of automocing, nothing needs to be
+done by the user. The tool usually detects the ``Q_OBJECT``
+macro and calls the "``moc``" executable accordingly.
+
+If you don't want this, you can switch off the automocing
+by a 
+
+::
+
+    env['QT5_AUTOSCAN'] = 0
+
+
+in your SConscript file. Then, you have to moc your files
+explicitly, using the Moc5 builder.
+
+You can also switch to an extended automoc strategy with
+
+::
+
+    env['QT5_AUTOSCAN_STRATEGY'] = 1
+
+
+Please read the description of the ``QT5_AUTOSCAN_STRATEGY``
+variable in the Reference manual for details.
+
+For debugging purposes, you can set the variable ``QT5_DEBUG``
+with
+
+::
+
+    env['QT5_DEBUG'] = 1
+
+
+which outputs a lot of messages during automocing.
+
+
+Forms (.ui)
+===========
+The header files with setup code for your GUI classes, are not
+compiled automatically from your ``.ui`` files. You always
+have to call the Uic5 builder explicitly like
+
+::
+
+    env.Uic5(Glob('*.ui'))
+    env.Program('foo', Glob('*.cpp'))
+
+
+
+Resource files (.qrc)
+=====================
+Resource files are not built automatically, you always
+have to add the names of the ``.qrc`` files to the source list
+for your program or library:
+
+::
+
+    env.Program('foo', Glob('*.cpp')+Glob('*.qrc'))
+
+
+For each of the Resource input files, its prefix defines the
+name of the resulting resource. An appropriate "``-name``" option
+is added to the call of the ``rcc`` executable
+by default.
+
+You can also call the Qrc5 builder explicitly as
+
+::
+
+    qrccc = env.Qrc5('foo') # ['foo.qrc'] -> ['qrc_foo.cc']
+
+
+or (overriding the default suffix)
+
+::
+
+    qrccc = env.Qrc5('myprefix_foo.cxx','foo.qrc') # -> ['qrc_myprefix_foo.cxx']
+
+
+and then add the resulting cxx file to the sources of your
+Program/Library:
+
+::
+
+    env.Program('foo', Glob('*.cpp') + qrccc)
+
+
+
+Translation files
+=================
+The update of the ``.ts`` files and the conversion to binary
+``.qm`` files is not done automatically. You have to call the
+corresponding builders on your own.
+
+Example for updating a translation file:
+
+::
+
+    env.Ts5('foo.ts','.') # -> ['foo.ts']
+
+
+By default, the ``.ts`` files are treated as *precious* targets. This means that
+they are not removed prior to a rebuild, but simply get updated. Additionally, they
+do not get cleaned on a "``scons -c``". If you want to delete the translation files
+on the "``-c``" SCons command, you can set the variable "``QT5_CLEAN_TS``" like this
+
+::
+
+    env['QT5_CLEAN_TS']=1
+
+
+Example for releasing a translation file, i.e. compiling
+it to a ``.qm`` binary file:
+
+::
+
+    env.Qm5('foo') # ['foo.ts'] -> ['foo.qm']
+
+
+or (overriding the output prefix)
+
+::
+
+    env.Qm5('myprefix','foo') # ['foo.ts'] -> ['myprefix.qm']
+
+
+As an extension both, the Ts5() and Qm5 builder, support the definition of
+multiple targets. So, calling
+
+::
+
+    env.Ts5(['app_en','app_de'], Glob('*.cpp'))
+
+
+and
+
+::
+
+    env.Qm5(['app','copy'], Glob('*.ts'))
+
+
+should work fine.
+
+Finally, two short notes about the support of directories for the Ts5() builder. You can
+pass an arbitrary mix of cxx files and subdirs to it, as in
+
+::
+
+    env.Ts5('app_en',['sub1','appwindow.cpp','main.cpp']))
+
+
+where ``sub1`` is a folder that gets scanned recursively for cxx files by ``lupdate``.
+But like this, you lose all dependency information for the subdir, i.e. if a file
+inside the folder changes, the .ts file is not updated automatically! In this case
+you should tell SCons to always update the target:
+
+::
+
+    ts = env.Ts5('app_en',['sub1','appwindow.cpp','main.cpp'])
+    env.AlwaysBuild(ts)
+
+
+Last note: specifying the current folder "``.``" as input to Ts5() and storing the resulting
+.ts file in the same directory, leads to a dependency cycle! You then have to store the .ts
+and .qm files outside of the current folder, or use ``Glob('*.cpp'))`` instead.
+
+
+
diff --git a/workspace/sconstools3/qt6/__init__.py b/workspace/sconstools3/qt6/__init__.py
new file mode 100644
index 0000000..12a1b96
--- /dev/null
+++ b/workspace/sconstools3/qt6/__init__.py
@@ -0,0 +1,1052 @@
+
+"""SCons.Tool.qt6
+
+Tool-specific initialization for Qt6.
+
+There normally shouldn't be any need to import this module directly.
+It will usually be imported through the generic SCons.Tool.Tool()
+selection method.
+
+"""
+
+#
+# Copyright (c) 2001-7,2010,2011,2012 The SCons Foundation
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+from __future__ import print_function
+
+import os.path
+import re
+import subprocess
+import sys
+
+import SCons.Action
+import SCons.Builder
+import SCons.Defaults
+import SCons.Scanner
+import SCons.Tool
+import SCons.Util
+import SCons.Script
+
+if hasattr(SCons.Warnings, "Warning"):
+    class ToolQt6Warning(SCons.Warnings.Warning):
+        pass
+else:
+    class ToolQt6Warning(SCons.Warnings.SConsWarning):
+        pass
+
+class GeneratedMocFileNotIncluded(ToolQt6Warning):
+    pass
+
+class QtdirNotFound(ToolQt6Warning):
+    pass
+
+SCons.Warnings.enableWarningClass(ToolQt6Warning)
+
+try:
+    sorted
+except NameError:
+    # Pre-2.4 Python has no sorted() function.
+    #
+    # The pre-2.4 Python list.sort() method does not support
+    # list.sort(key=) nor list.sort(reverse=) keyword arguments, so
+    # we must implement the functionality of those keyword arguments
+    # by hand instead of passing them to list.sort().
+    def sorted(iterable, cmp=None, key=None, reverse=0):
+        if key is not None:
+            result = [(key(x), x) for x in iterable]
+        else:
+            result = iterable[:]
+        if cmp is None:
+            # Pre-2.3 Python does not support list.sort(None).
+            result.sort()
+        else:
+            result.sort(cmp)
+        if key is not None:
+            result = [t1 for t0,t1 in result]
+        if reverse:
+            result.reverse()
+        return result
+
+def _contents_regex(e):
+    # get_contents() of scons nodes returns a binary buffer, so we convert the regexes also to binary here
+    # this won't work for specific encodings like UTF-16, but most of the time we will be fine here.
+    # note that the regexes used here are always pure ascii, so we don't have an issue here.
+    if sys.version_info.major >= 3:
+        e = e.encode('ascii')
+    return e
+
+qrcinclude_re = re.compile(r'<file[^>]*>([^<]*)</file>', re.M)
+
+mocver_re = re.compile(_contents_regex(r'.*(\d+)\.(\d+)\.(\d+).*'))
+
+def transformToWinePath(path) :
+    return os.popen('winepath -w "%s"'%path).read().strip().replace('\\','/')
+
+header_extensions = [".h", ".hxx", ".hpp", ".hh"]
+if SCons.Util.case_sensitive_suffixes('.h', '.H'):
+    header_extensions.append('.H')
+# TODO: The following two lines will work when integrated back to SCons
+# TODO: Meanwhile the third line will do the work
+#cplusplus = __import__('c++', globals(), locals(), [])
+#cxx_suffixes = cplusplus.CXXSuffixes
+cxx_suffixes = [".c", ".cxx", ".cpp", ".cc"]
+
+def checkMocIncluded(target, source, env):
+    moc = target[0]
+    cpp = source[0]
+    # looks like cpp.includes is cleared before the build stage :-(
+    # not really sure about the path transformations (moc.cwd? cpp.cwd?) :-/
+    path = SCons.Defaults.CScan.path_function(env, moc.cwd)
+    includes = SCons.Defaults.CScan(cpp, env, path)
+    if not moc in includes:
+        SCons.Warnings.warn(
+            GeneratedMocFileNotIncluded,
+            "Generated moc file '%s' is not included by '%s'" %
+            (str(moc), str(cpp)))
+
+def find_file(filename, paths, node_factory):
+    for dir in paths:
+        node = node_factory(filename, dir)
+        if node.rexists():
+            return node
+    return None
+
+class _Automoc:
+    """
+    Callable class, which works as an emitter for Programs, SharedLibraries and
+    StaticLibraries.
+    """
+
+    def __init__(self, objBuilderName):
+        self.objBuilderName = objBuilderName
+        # some regular expressions:
+        # Q_OBJECT detection
+        self.qo_search = re.compile(_contents_regex(r'[^A-Za-z0-9](Q_OBJECT)|(Q_GADGET)[^A-Za-z0-9]'))
+        # cxx and c comment 'eater'
+        self.ccomment = re.compile(_contents_regex(r'/\*(.*?)\*/'),re.S)
+        self.cxxcomment = re.compile(_contents_regex(r'//.*$'),re.M)
+        # we also allow Q_OBJECT in a literal string
+        self.literal_qobject = re.compile(_contents_regex(r'"[^\n]*(Q_OBJECT)|(Q_GADGET)[^\n]*"'))
+        
+    def create_automoc_options(self, env):
+        """
+        Create a dictionary with variables related to Automocing,
+        based on the current environment.
+        Is executed once in the __call__ routine.  
+        """
+        moc_options = {'auto_scan' : True,
+                       'auto_scan_strategy' : 0,
+                       'gobble_comments' : 0,
+                       'debug' : 0,
+                       'auto_cpppath' : True,
+                       'cpppaths' : []}
+        try:
+            if int(env.subst('$QT6_AUTOSCAN')) == 0:
+                moc_options['auto_scan'] = False
+        except ValueError:
+            pass
+        try:
+            moc_options['auto_scan_strategy'] = int(env.subst('$QT6_AUTOSCAN_STRATEGY'))
+        except ValueError:
+            pass
+        try:
+            moc_options['gobble_comments'] = int(env.subst('$QT6_GOBBLECOMMENTS'))
+        except ValueError:
+            pass
+        try:
+            moc_options['debug'] = int(env.subst('$QT6_DEBUG'))
+            if moc_options["debug"]:
+                print("Automoc debugging turned on.")
+        except ValueError:
+            pass
+        try:
+            if int(env.subst('$QT6_AUTOMOC_SCANCPPPATH')) == 0:
+                moc_options['auto_cpppath'] = False
+        except ValueError:
+            pass
+        if moc_options['auto_cpppath']:
+            paths = env.get('QT6_AUTOMOC_CPPPATH', [])
+            if not paths:
+                paths = env.get('CPPPATH', [])
+            moc_options['cpppaths'].extend(paths)
+        
+        return moc_options
+
+    def __automoc_strategy_simple(self, env, moc_options, 
+                                  cpp, cpp_contents, out_sources):
+        """
+        Default Automoc strategy (Q_OBJECT driven): detect a header file
+        (alongside the current cpp/cxx) that contains a Q_OBJECT
+        macro...and MOC it.
+        If a Q_OBJECT macro is also found in the cpp/cxx itself,
+        it gets MOCed too.
+        """
+        
+        h=None
+        for h_ext in header_extensions:
+            # try to find the header file in the corresponding source
+            # directory
+            hname = self.splitext(cpp.name)[0] + h_ext
+            h = find_file(hname, [cpp.get_dir()]+moc_options['cpppaths'], env.File)
+            if h:
+                if moc_options['debug']:
+                    print("scons: qt6: Scanning '%s' (header of '%s')" % (str(h), str(cpp)))
+                h_contents = h.get_contents()
+                if moc_options['gobble_comments']:
+                    h_contents = self.ccomment.sub(_contents_regex(''), h_contents)
+                    h_contents = self.cxxcomment.sub(_contents_regex(''), h_contents)
+                h_contents = self.literal_qobject.sub(_contents_regex('""'), h_contents)
+                break
+        if not h and moc_options['debug']:
+            print("scons: qt6: no header for '%s'." % (str(cpp)))
+        if h and self.qo_search.search(h_contents):
+            # h file with the Q_OBJECT macro found -> add moc_cpp
+            moc_cpp = env.Moc6(h)
+            if moc_options['debug']:
+                print("scons: qt6: found Q_OBJECT macro in '%s', moc'ing to '%s'" % (str(h), str(moc_cpp)))
+            
+            # Now, check whether the corresponding CPP file
+            # includes the moc'ed output directly...
+            inc_moc_cpp = _contents_regex(r'^\s*#\s*include\s+"%s"' % str(moc_cpp[0]))
+            if cpp and re.search(inc_moc_cpp, cpp_contents, re.M):
+                if moc_options['debug']:
+                    print("scons: qt6: CXX file '%s' directly includes the moc'ed output '%s', no compiling required" % (str(cpp), str(moc_cpp)))
+                env.Depends(cpp, moc_cpp)
+            else:
+                moc_o = self.objBuilder(moc_cpp)
+                if moc_options['debug']:
+                    print("scons: qt6: compiling '%s' to '%s'" % (str(cpp), str(moc_o)))
+                out_sources.extend(moc_o)
+        if cpp and self.qo_search.search(cpp_contents):
+            # cpp file with Q_OBJECT macro found -> add moc
+            # (to be included in cpp)
+            moc = env.Moc6(cpp)
+            env.Ignore(moc, moc)
+            if moc_options['debug']:
+                print("scons: qt6: found Q_OBJECT macro in '%s', moc'ing to '%s'" % (str(cpp), str(moc)))
+
+    def __automoc_strategy_include_driven(self, env, moc_options,
+                                          cpp, cpp_contents, out_sources):
+        """
+        Automoc strategy #1 (include driven): searches for "include"
+        statements of MOCed files in the current cpp/cxx file.
+        This strategy tries to add support for the compilation
+        of the qtsolutions...
+        """
+        if self.splitext(str(cpp))[1] in cxx_suffixes:
+            added = False
+            h_moc = "%s%s%s" % (env.subst('$QT6_XMOCHPREFIX'),
+                                self.splitext(cpp.name)[0],
+                                env.subst('$QT6_XMOCHSUFFIX'))
+            cxx_moc = "%s%s%s" % (env.subst('$QT6_XMOCCXXPREFIX'),
+                                  self.splitext(cpp.name)[0],
+                                  env.subst('$QT6_XMOCCXXSUFFIX'))
+            inc_h_moc = _contents_regex(r'#include\s+"%s"' % h_moc)
+            inc_cxx_moc = _contents_regex(r'#include\s+"%s"' % cxx_moc)
+            
+            # Search for special includes in qtsolutions style
+            if cpp and re.search(inc_h_moc, cpp_contents):
+                # cpp file with #include directive for a MOCed header found -> add moc
+                
+                # Try to find header file                    
+                h=None
+                hname=""
+                for h_ext in header_extensions:
+                    # Try to find the header file in the
+                    # corresponding source directory
+                    hname = self.splitext(cpp.name)[0] + h_ext
+                    h = find_file(hname, [cpp.get_dir()]+moc_options['cpppaths'], env.File)
+                    if h:
+                        if moc_options['debug']:
+                            print("scons: qt6: Scanning '%s' (header of '%s')" % (str(h), str(cpp)))
+                        h_contents = h.get_contents()
+                        if moc_options['gobble_comments']:
+                            h_contents = self.ccomment.sub('', h_contents)
+                            h_contents = self.cxxcomment.sub('', h_contents)
+                        h_contents = self.literal_qobject.sub('""', h_contents)
+                        break
+                if not h and moc_options['debug']:
+                    print("scons: qt6: no header for '%s'." % (str(cpp)))
+                if h and self.qo_search.search(h_contents):
+                    # h file with the Q_OBJECT macro found -> add moc_cpp
+                    moc_cpp = env.XMoc6(h)
+                    env.Ignore(moc_cpp, moc_cpp)
+                    added = True
+                    # Removing file from list of sources, because it is not to be
+                    # compiled but simply included by the cpp/cxx file.
+                    for idx, s in enumerate(out_sources):
+                        if hasattr(s, "sources") and len(s.sources) > 0:
+                            if str(s.sources[0]) == h_moc:
+                                out_sources.pop(idx)
+                                break
+                    if moc_options['debug']:
+                        print("scons: qt6: found Q_OBJECT macro in '%s', moc'ing to '%s'" % (str(h), str(h_moc)))
+                else:
+                    if moc_options['debug']:
+                        print("scons: qt6: found no Q_OBJECT macro in '%s', but a moc'ed version '%s' gets included in '%s'" % (str(h), inc_h_moc, cpp.name))
+
+            if cpp and re.search(inc_cxx_moc, cpp_contents):
+                # cpp file with #include directive for a MOCed cxx file found -> add moc
+                if self.qo_search.search(cpp_contents):
+                    moc = env.XMoc6(target=cxx_moc, source=cpp)
+                    env.Ignore(moc, moc)
+                    added = True
+                    if moc_options['debug']:
+                        print("scons: qt6: found Q_OBJECT macro in '%s', moc'ing to '%s'" % (str(cpp), str(moc)))
+                else:
+                    if moc_options['debug']:
+                        print("scons: qt6: found no Q_OBJECT macro in '%s', although a moc'ed version '%s' of itself gets included" % (cpp.name, inc_cxx_moc))
+
+            if not added:
+                # Fallback to default Automoc strategy (Q_OBJECT driven)
+               self.__automoc_strategy_simple(env, moc_options, cpp,
+                                              cpp_contents, out_sources)
+        
+    def __call__(self, target, source, env):
+        """
+        Smart autoscan function. Gets the list of objects for the Program
+        or Lib. Adds objects and builders for the special qt6 files.
+        """
+        moc_options = self.create_automoc_options(env)
+        
+        # some shortcuts used in the scanner
+        self.splitext = SCons.Util.splitext
+        self.objBuilder = getattr(env, self.objBuilderName)
+
+        # The following is kind of hacky to get builders working properly (FIXME)
+        objBuilderEnv = self.objBuilder.env
+        self.objBuilder.env = env
+        mocBuilderEnv = env.Moc6.env
+        env.Moc6.env = env
+        xMocBuilderEnv = env.XMoc6.env
+        env.XMoc6.env = env
+        
+        # make a deep copy for the result; MocH objects will be appended
+        out_sources = source[:]
+
+        for obj in source:
+            if not moc_options['auto_scan']:
+                break
+            if isinstance(obj,str):  # big kludge!
+                print("scons: qt6: '%s' MAYBE USING AN OLD SCONS VERSION AND NOT CONVERTED TO 'File'. Discarded." % str(obj))
+                continue
+            if not obj.has_builder():
+                # binary obj file provided
+                if moc_options['debug']:
+                    print("scons: qt6: '%s' seems to be a binary. Discarded." % str(obj))
+                continue
+            cpp = obj.sources[0]
+            if not self.splitext(str(cpp))[1] in cxx_suffixes:
+                if moc_options['debug']:
+                    print("scons: qt6: '%s' is no cxx file. Discarded." % str(cpp)) 
+                # c or fortran source
+                continue
+            try:
+                cpp_contents = cpp.get_contents()
+                if moc_options['gobble_comments']:
+                    cpp_contents = self.ccomment.sub('', cpp_contents)
+                    cpp_contents = self.cxxcomment.sub('', cpp_contents)
+                cpp_contents = self.literal_qobject.sub('""', cpp_contents)
+            except: continue # may be an still not generated source
+            
+            if moc_options['auto_scan_strategy'] == 0:
+                # Default Automoc strategy (Q_OBJECT driven)
+                self.__automoc_strategy_simple(env, moc_options,
+                                               cpp, cpp_contents, out_sources)
+            else:
+                # Automoc strategy #1 (include driven)
+                self.__automoc_strategy_include_driven(env, moc_options,
+                                                       cpp, cpp_contents, out_sources)
+
+        # restore the original env attributes (FIXME)
+        self.objBuilder.env = objBuilderEnv
+        env.Moc6.env = mocBuilderEnv
+        env.XMoc6.env = xMocBuilderEnv
+
+        # We return the set of source entries as sorted sequence, else
+        # the order might accidentally change from one build to another
+        # and trigger unwanted rebuilds. For proper sorting, a key function
+        # has to be specified...FS.Entry (and Base nodes in general) do not
+        # provide a __cmp__, for performance reasons. 
+        return (target, sorted(set(out_sources), key=lambda entry : str(entry)))
+
+AutomocShared = _Automoc('SharedObject')
+AutomocStatic = _Automoc('StaticObject')
+
+def _detect(env):
+    """Not really safe, but fast method to detect the Qt6 library"""
+    try: return env['QT6DIR']
+    except KeyError: pass
+
+    try: return env['QTDIR']
+    except KeyError: pass
+
+    try: return os.environ['QT6DIR']
+    except KeyError: pass
+
+    try: return os.environ['QTDIR']
+    except KeyError: pass
+
+    moc = env.WhereIs('moc-qt6') or env.WhereIs('moc6') or env.WhereIs('moc')
+    if moc:
+        vernumber = subprocess.check_output([moc, '-v'], stderr=subprocess.STDOUT)
+        vernumber = mocver_re.match(vernumber)
+        if vernumber:
+            vernumber = [ int(x) for x in vernumber.groups() ]
+            if vernumber < [6, 0, 0]:
+                vernumber = '.'.join([str(x) for x in vernumber])
+                moc = None
+                SCons.Warnings.warn(
+                    QtdirNotFound,
+                    "QT6DIR variable not defined, and detected moc is for Qt %s" % vernumber)
+        if moc is not None:
+          print("moc=%s" % moc)
+          QT6DIR = os.path.dirname(os.path.dirname(moc))
+          SCons.Warnings.warn(
+              QtdirNotFound,
+              "QT6DIR variable is not defined, using moc executable as a hint (QT6DIR=%s)" % QT6DIR)
+          return QT6DIR
+
+    raise SCons.Errors.StopError(
+        QtdirNotFound,
+        "Could not detect Qt 6 installation")
+    return None
+
+
+def __scanResources(node, env, path, arg):
+    # Helper function for scanning .qrc resource files
+    # I've been careful on providing names relative to the qrc file
+    # If that was not needed this code could be simplified a lot
+    def recursiveFiles(basepath, path) :
+        result = []
+        for item in os.listdir(os.path.join(basepath, path)) :
+            itemPath = os.path.join(path, item)
+            if os.path.isdir(os.path.join(basepath, itemPath)) :
+                result += recursiveFiles(basepath, itemPath)
+            else:
+                result.append(itemPath)
+        return result
+    contents = node.get_contents()
+    if sys.version_info.major >= 3:
+        # we assume the default xml encoding (utf-8) here
+        contents = contents.decode('utf-8')
+    includes = qrcinclude_re.findall(contents)
+    qrcpath = os.path.dirname(node.path)
+    dirs = [included for included in includes if os.path.isdir(os.path.join(qrcpath,included))]
+    # dirs need to include files recursively
+    for dir in dirs :
+        includes.remove(dir)
+        includes+=recursiveFiles(qrcpath,dir)
+    return includes
+
+#
+# Scanners
+#
+__qrcscanner = SCons.Scanner.Scanner(name = 'qrcfile',
+    function = __scanResources,
+    argument = None,
+    skeys = ['.qrc'])
+
+#
+# Emitters
+#
+def __qrc_path(head, prefix, tail, suffix):
+    if head:
+        if tail:
+            return os.path.join(head, "%s%s%s" % (prefix, tail, suffix))
+        else:
+            return "%s%s%s" % (prefix, head, suffix)
+    else:
+        return "%s%s%s" % (prefix, tail, suffix)
+def __qrc_emitter(target, source, env):
+    sourceBase, sourceExt = os.path.splitext(SCons.Util.to_String(source[0]))
+    sHead = None
+    sTail = sourceBase
+    if sourceBase:
+        sHead, sTail = os.path.split(sourceBase)
+
+    t = __qrc_path(sHead, env.subst('$QT6_QRCCXXPREFIX'),
+                   sTail, env.subst('$QT6_QRCCXXSUFFIX'))
+
+    return t, source
+
+#
+# Action generators
+#
+def __moc_generator_from_h(source, target, env, for_signature):
+    pass_defines = False
+    try:
+        if int(env.subst('$QT6_CPPDEFINES_PASSTOMOC')) == 1:
+            pass_defines = True
+    except ValueError:
+        pass
+    
+    if pass_defines:
+        return '$QT6_MOC $QT6_MOCDEFINES $QT6_MOCFROMHFLAGS $QT6_MOCINCFLAGS -o $TARGET $SOURCE'
+    else:
+        return '$QT6_MOC $QT6_MOCFROMHFLAGS $QT6_MOCINCFLAGS -o $TARGET $SOURCE'
+
+def __moc_generator_from_cxx(source, target, env, for_signature):
+    pass_defines = False
+    try:
+        if int(env.subst('$QT6_CPPDEFINES_PASSTOMOC')) == 1:
+            pass_defines = True
+    except ValueError:
+        pass
+    if pass_defines:
+        return ['$QT6_MOC $QT6_MOCDEFINES $QT6_MOCFROMCXXFLAGS $QT6_MOCINCFLAGS -o $TARGET $SOURCE',
+                SCons.Action.Action(checkMocIncluded,None)]
+    else:
+        return ['$QT6_MOC $QT6_MOCFROMCXXFLAGS $QT6_MOCINCFLAGS -o $TARGET $SOURCE',
+                SCons.Action.Action(checkMocIncluded,None)]
+
+def __mocx_generator_from_h(source, target, env, for_signature):
+    pass_defines = False
+    try:
+        if int(env.subst('$QT6_CPPDEFINES_PASSTOMOC')) == 1:
+            pass_defines = True
+    except ValueError:
+        pass
+    
+    if pass_defines:
+        return '$QT6_MOC $QT6_MOCDEFINES $QT6_MOCFROMHFLAGS $QT6_MOCINCFLAGS -o $TARGET $SOURCE'
+    else:
+        return '$QT6_MOC $QT6_MOCFROMHFLAGS $QT6_MOCINCFLAGS -o $TARGET $SOURCE'
+
+def __mocx_generator_from_cxx(source, target, env, for_signature):
+    pass_defines = False
+    try:
+        if int(env.subst('$QT6_CPPDEFINES_PASSTOMOC')) == 1:
+            pass_defines = True
+    except ValueError:
+        pass
+    
+    if pass_defines:
+        return ['$QT6_MOC $QT6_MOCDEFINES $QT6_MOCFROMCXXFLAGS $QT6_MOCINCFLAGS -o $TARGET $SOURCE',
+                SCons.Action.Action(checkMocIncluded,None)]
+    else:
+        return ['$QT6_MOC $QT6_MOCFROMCXXFLAGS $QT6_MOCINCFLAGS -o $TARGET $SOURCE',
+                SCons.Action.Action(checkMocIncluded,None)]
+
+def __qrc_generator(source, target, env, for_signature):
+    name_defined = False
+    try:
+        if env.subst('$QT6_QRCFLAGS').find('-name') >= 0:
+            name_defined = True
+    except ValueError:
+        pass
+    
+    if name_defined:
+        return '$QT6_RCC $QT6_QRCFLAGS $SOURCE -o $TARGET'
+    else:
+        qrc_suffix = env.subst('$QT6_QRCSUFFIX')
+        src = str(source[0])
+        head, tail = os.path.split(src)
+        if tail:
+            src = tail
+        qrc_suffix = env.subst('$QT6_QRCSUFFIX')
+        if src.endswith(qrc_suffix):
+            qrc_stem = src[:-len(qrc_suffix)]
+        else:
+            qrc_stem = src
+        return '$QT6_RCC $QT6_QRCFLAGS -name %s $SOURCE -o $TARGET' % qrc_stem
+
+class MocSourceScanner:
+    def __init__(self):
+        self.exclude_builder = None
+
+    # Scanner function, exclude the built file from the dependencies
+    def __call__(self, node, env, path):
+        args = (node, env, path)
+        #print("scan_func(%s)" % ([str(x) for x in args]))
+        res = SCons.Script.SourceFileScanner(*args)
+        #print([(x.builder, self.exclude_builder, x.builder.get_name(env)) for x in res if x.has_builder()])
+        res = [x for x in res if (not x.has_builder()) or (x.builder is not self.exclude_builder)]
+        #print([str(x) for x in res])
+        return res
+
+
+#
+# Builders
+#
+__ts_builder = SCons.Builder.Builder(        
+        action = SCons.Action.Action('$QT6_LUPDATECOM','$QT6_LUPDATECOMSTR'),
+        suffix = '.ts',
+        source_factory = SCons.Node.FS.Entry)
+__qm_builder = SCons.Builder.Builder(
+        action = SCons.Action.Action('$QT6_LRELEASECOM','$QT6_LRELEASECOMSTR'),
+        src_suffix = '.ts',
+        suffix = '.qm')
+__qrc_builder = SCons.Builder.Builder(
+        action = SCons.Action.CommandGeneratorAction(__qrc_generator, {'cmdstr':'$QT6_QRCCOMSTR'}),
+        source_scanner = __qrcscanner,
+        src_suffix = '$QT6_QRCSUFFIX',
+        suffix = '$QT6_QRCCXXSUFFIX',
+        prefix = '$QT6_QRCCXXPREFIX',
+        single_source = 1)
+__ex_moc_scanner = MocSourceScanner()
+__ex_moc_builder = SCons.Builder.Builder(
+        action = SCons.Action.CommandGeneratorAction(__moc_generator_from_h, {'cmdstr':'$QT6_MOCCOMSTR'}),
+        source_scanner = SCons.Scanner.Scanner(function=__ex_moc_scanner)
+        )
+__ex_moc_scanner.exclude_builder = __ex_moc_builder
+
+__ex_uic_builder = SCons.Builder.Builder(
+        action = SCons.Action.Action('$QT6_UICCOM', '$QT6_UICCOMSTR'),
+        src_suffix = '.ui')
+
+
+#
+# Wrappers (pseudo-Builders)
+#
+def Ts6(env, target, source=None, *args, **kw):
+    """
+    A pseudo-Builder wrapper around the LUPDATE executable of Qt6.
+        lupdate [options] [source-file|path]... -ts ts-files
+    """
+    if not SCons.Util.is_List(target):
+        target = [target]
+    if not source:
+        source = target[:]
+    if not SCons.Util.is_List(source):
+        source = [source]
+
+    # Check QT6_CLEAN_TS and use NoClean() function
+    clean_ts = False
+    try:
+        if int(env.subst('$QT6_CLEAN_TS')) == 1:
+            clean_ts = True
+    except ValueError:
+        pass
+    
+    result = []
+    for t in target:
+        obj = __ts_builder.__call__(env, t, source, **kw)
+        # Prevent deletion of the .ts file, unless explicitly specified
+        if not clean_ts:
+            env.NoClean(obj)
+        # Always make our target "precious", such that it is not deleted
+        # prior to a rebuild
+        env.Precious(obj)
+        # Add to resulting target list        
+        result.extend(obj)
+
+    return result
+
+def Qm6(env, target, source=None, *args, **kw):
+    """
+    A pseudo-Builder wrapper around the LRELEASE executable of Qt6.
+        lrelease [options] ts-files [-qm qm-file]
+    """
+    if not SCons.Util.is_List(target):
+        target = [target]
+    if not source:
+        source = target[:]
+    if not SCons.Util.is_List(source):
+        source = [source]
+
+    result = []    
+    for t in target:
+        result.extend(__qm_builder.__call__(env, t, source, **kw))
+
+    return result
+
+def Qrc6(env, target, source=None, *args, **kw):
+    """
+    A pseudo-Builder wrapper around the RCC executable of Qt6.
+        rcc [options] qrc-files -o out-file
+    """
+    if not SCons.Util.is_List(target):
+        target = [target]
+    if not source:
+        source = target[:]
+    if not SCons.Util.is_List(source):
+        source = [source]
+
+    result = []
+    for t, s in zip(target, source):
+        result.extend(__qrc_builder.__call__(env, t, s, **kw))
+
+    return result
+
+def ExplicitMoc6(env, target, source, *args, **kw):
+    """
+    A pseudo-Builder wrapper around the MOC executable of Qt6.
+        moc [options] <header-file>
+    """
+    if not SCons.Util.is_List(target):
+        target = [target]
+    if not SCons.Util.is_List(source):
+        source = [source]
+
+    result = []
+    for t in target:
+        # Is it a header or a cxx file?
+        result.extend(__ex_moc_builder.__call__(env, t, source, **kw))
+
+    return result
+
+def ExplicitUic6(env, target, source, *args, **kw):
+    """
+    A pseudo-Builder wrapper around the UIC executable of Qt6.
+        uic [options] <uifile>
+    """
+    if not SCons.Util.is_List(target):
+        target = [target]
+    if not SCons.Util.is_List(source):
+        source = [source]
+
+    result = []
+    for t in target:
+        result.extend(__ex_uic_builder.__call__(env, t, source, **kw))
+
+    return result
+
+def generate(env):
+    """Add Builders and construction variables for qt6 to an Environment."""
+
+    suffixes = [
+        '-qt6',
+        '-qt6.exe',
+        '6',
+        '6.exe',
+        '',
+        '.exe',
+    ]
+    command_suffixes = ['-qt6', '6', '']
+        
+    def locateQt6Command(env, command, qtdir) :
+        triedPaths = []
+        for suffix in suffixes :
+            for subdir in ["bin", "libexec"]:
+                fullpath = os.path.join(qtdir,subdir,command + suffix)
+                if os.access(fullpath, os.X_OK) :
+                    return fullpath
+                triedPaths.append(fullpath)
+
+        fullpath = env.Detect([command+s for s in command_suffixes])
+        if not (fullpath is None) : return fullpath
+
+        raise Exception("Qt6 command '" + command + "' not found. Tried: " + ', '.join(triedPaths))
+
+    CLVar = SCons.Util.CLVar
+    Action = SCons.Action.Action
+    Builder = SCons.Builder.Builder
+
+    env['QT6DIR']  = _detect(env)
+    # TODO: 'Replace' should be 'SetDefault'
+    env.SetDefault(
+#    env.Replace(
+        QT6DIR  = _detect(env),
+        QT6_BINPATH = os.path.join('$QT6DIR', 'bin'),
+        # TODO: This is not reliable to QT6DIR value changes but needed in order to support '-qt6' variants
+        QT6_MOC = locateQt6Command(env,'moc', env['QT6DIR']),
+        QT6_UIC = locateQt6Command(env,'uic', env['QT6DIR']),
+        QT6_RCC = locateQt6Command(env,'rcc', env['QT6DIR']),
+        QT6_LUPDATE = locateQt6Command(env,'lupdate', env['QT6DIR']),
+        QT6_LRELEASE = locateQt6Command(env,'lrelease', env['QT6DIR']),
+
+        QT6_AUTOSCAN = 1, # Should the qt6 tool try to figure out, which sources are to be moc'ed?
+        QT6_AUTOSCAN_STRATEGY = 0, # While scanning for files to moc, should we search for includes in qtsolutions style?
+        QT6_GOBBLECOMMENTS = 0, # If set to 1, comments are removed before scanning cxx/h files.
+        QT6_CPPDEFINES_PASSTOMOC = 1, # If set to 1, all CPPDEFINES get passed to the moc executable.
+        QT6_CLEAN_TS = 0, # If set to 1, translation files (.ts) get cleaned on 'scons -c'
+        QT6_AUTOMOC_SCANCPPPATH = 1, # If set to 1, the CPPPATHs (or QT6_AUTOMOC_CPPPATH) get scanned for moc'able files
+        QT6_AUTOMOC_CPPPATH = [], # Alternative paths that get scanned for moc files
+
+        # Some Qt6 specific flags. I don't expect someone wants to
+        # manipulate those ...
+        QT6_UICFLAGS = CLVar(''),
+        QT6_MOCFROMHFLAGS = CLVar(''),
+        QT6_MOCFROMCXXFLAGS = CLVar('-i'),
+        QT6_QRCFLAGS = '',
+        QT6_LUPDATEFLAGS = '',
+        QT6_LRELEASEFLAGS = '',
+
+        # suffixes/prefixes for the headers / sources to generate
+        QT6_UISUFFIX = '.ui',
+        QT6_UICDECLPREFIX = 'ui_',
+        QT6_UICDECLSUFFIX = '.h',
+        QT6_MOCINCPREFIX = '-I',
+        QT6_MOCHPREFIX = 'moc_',
+        QT6_MOCHSUFFIX = '$CXXFILESUFFIX',
+        QT6_MOCCXXPREFIX = '',
+        QT6_MOCCXXSUFFIX = '.moc',
+        QT6_QRCSUFFIX = '.qrc',
+        QT6_QRCCXXSUFFIX = '$CXXFILESUFFIX',
+        QT6_QRCCXXPREFIX = 'qrc_',
+        QT6_MOCDEFPREFIX = '-D',
+        QT6_MOCDEFSUFFIX = '',
+        QT6_MOCDEFINES = '${_defines(QT6_MOCDEFPREFIX, CPPDEFINES, QT6_MOCDEFSUFFIX, __env__, TARGET, SOURCE)}',
+        QT6_MOCCPPPATH = [],
+        QT6_MOCINCFLAGS = '$( ${_concat(QT6_MOCINCPREFIX, QT6_MOCCPPPATH, INCSUFFIX, __env__, RDirs)} $)',
+
+        # Commands for the qt6 support ...
+        QT6_UICCOM = '$QT6_UIC $QT6_UICFLAGS -o $TARGET $SOURCE',
+        QT6_LUPDATECOM = '$QT6_LUPDATE $QT6_LUPDATEFLAGS $SOURCES -ts $TARGET',
+        QT6_LRELEASECOM = '$QT6_LRELEASE $QT6_LRELEASEFLAGS -qm $TARGET $SOURCES',
+        
+        # Specialized variables for the Extended Automoc support
+        # (Strategy #1 for qtsolutions)
+        QT6_XMOCHPREFIX = 'moc_',
+        QT6_XMOCHSUFFIX = '.cpp',
+        QT6_XMOCCXXPREFIX = '',
+        QT6_XMOCCXXSUFFIX = '.moc',
+                
+        )
+
+    try:
+        env.AddMethod(Ts6, "Ts6")
+        env.AddMethod(Qm6, "Qm6")
+        env.AddMethod(Qrc6, "Qrc6")
+        env.AddMethod(ExplicitMoc6, "ExplicitMoc6")
+        env.AddMethod(ExplicitUic6, "ExplicitUic6")
+    except AttributeError:
+        # Looks like we use a pre-0.98 version of SCons...
+        from SCons.Script.SConscript import SConsEnvironment
+        SConsEnvironment.Ts6 = Ts6
+        SConsEnvironment.Qm6 = Qm6
+        SConsEnvironment.Qrc6 = Qrc6
+        SConsEnvironment.ExplicitMoc6 = ExplicitMoc6
+        SConsEnvironment.ExplicitUic6 = ExplicitUic6
+
+    # Interface builder
+    uic6builder = Builder(
+        action = SCons.Action.Action('$QT6_UICCOM', '$QT6_UICCOMSTR'),
+        src_suffix='$QT6_UISUFFIX',
+        suffix='$QT6_UICDECLSUFFIX',
+        prefix='$QT6_UICDECLPREFIX',
+        single_source = True
+        #TODO: Consider the uiscanner on new scons version
+        )
+    env['BUILDERS']['Uic6'] = uic6builder
+
+    # Metaobject builder
+    mocScanner = MocSourceScanner()
+    mocBld = Builder(action={}, prefix={}, suffix={}, source_scanner=SCons.Scanner.Scanner(function=mocScanner))
+    for h in header_extensions:
+        act = SCons.Action.CommandGeneratorAction(__moc_generator_from_h, {'cmdstr':'$QT6_MOCCOMSTR'})    
+        mocBld.add_action(h, act)
+        mocBld.prefix[h] = '$QT6_MOCHPREFIX'
+        mocBld.suffix[h] = '$QT6_MOCHSUFFIX'
+    for cxx in cxx_suffixes:
+        act = SCons.Action.CommandGeneratorAction(__moc_generator_from_cxx, {'cmdstr':'$QT6_MOCCOMSTR'})    
+        mocBld.add_action(cxx, act)
+        mocBld.prefix[cxx] = '$QT6_MOCCXXPREFIX'
+        mocBld.suffix[cxx] = '$QT6_MOCCXXSUFFIX'
+    env['BUILDERS']['Moc6'] = mocBld
+    mocScanner.exclude_builder = mocBld.builder
+
+    # Metaobject builder for the extended auto scan feature 
+    # (Strategy #1 for qtsolutions)
+    xMocBld = Builder(action={}, prefix={}, suffix={})
+    for h in header_extensions:
+        act = SCons.Action.CommandGeneratorAction(__mocx_generator_from_h, {'cmdstr':'$QT6_MOCCOMSTR'})
+        xMocBld.add_action(h, act)
+        xMocBld.prefix[h] = '$QT6_XMOCHPREFIX'
+        xMocBld.suffix[h] = '$QT6_XMOCHSUFFIX'
+    for cxx in cxx_suffixes:
+        act = SCons.Action.CommandGeneratorAction(__mocx_generator_from_cxx, {'cmdstr':'$QT6_MOCCOMSTR'})    
+        xMocBld.add_action(cxx, act)
+        xMocBld.prefix[cxx] = '$QT6_XMOCCXXPREFIX'
+        xMocBld.suffix[cxx] = '$QT6_XMOCCXXSUFFIX'
+    env['BUILDERS']['XMoc6'] = xMocBld
+
+    # Add the Qrc6 action to the CXX file builder (registers the
+    # *.qrc extension with the Environment)
+    cfile_builder, cxxfile_builder = SCons.Tool.createCFileBuilders(env)
+    qrc_act = SCons.Action.CommandGeneratorAction(__qrc_generator, {'cmdstr':'$QT6_QRCCOMSTR'})
+    cxxfile_builder.add_action('$QT6_QRCSUFFIX', qrc_act)
+    cxxfile_builder.add_emitter('$QT6_QRCSUFFIX', __qrc_emitter)
+
+    # We use the emitters of Program / StaticLibrary / SharedLibrary
+    # to scan for moc'able files
+    # We can't refer to the builders directly, we have to fetch them
+    # as Environment attributes because that sets them up to be called
+    # correctly later by our emitter.
+    env.AppendUnique(PROGEMITTER =[AutomocStatic],
+                     SHLIBEMITTER=[AutomocShared],
+                     LIBEMITTER  =[AutomocStatic],
+                    )
+
+    # TODO: Does dbusxml2cpp need an adapter
+    try:
+        env.AddMethod(enable_modules, "EnableQt6Modules")
+    except AttributeError:
+        # Looks like we use a pre-0.98 version of SCons...
+        from SCons.Script.SConscript import SConsEnvironment
+        SConsEnvironment.EnableQt6Modules = enable_modules
+
+def enable_modules(self, modules, debug=False, crosscompiling=False) :
+    import sys
+
+    validModules = [
+        # Qt Essentials
+        'QtCore',
+        'QtGui',
+        'QtMultimedia',
+        'QtMultimediaQuick_p',
+        'QtMultimediaWidgets',
+        'QtNetwork',
+        'QtPlatformSupport',
+        'QtQml',
+        'QtQmlDevTools',
+        'QtQuick',
+        'QtQuickParticles',
+        'QtSql',
+        'QtQuickTest',
+        'QtTest',
+        'QtWebKit',
+        'QtWebKitWidgets',
+        'QtWidgets',
+        # Qt Add-Ons
+        'QtConcurrent',
+        'QtDBus',
+        'QtOpenGL',
+        'QtPrintSupport',
+        'QtDeclarative',
+        'QtScript',
+        'QtScriptTools',
+        'QtSvg',
+        'QtUiTools',
+        'QtXml',
+        'QtXmlPatterns',
+        # Qt Tools
+        'QtHelp',
+        'QtDesigner',
+        'QtDesignerComponents',
+        # Other
+        'QtCLucene',
+        'QtConcurrent',
+        'QtV8'
+        ]
+    pclessModules = [
+    ]
+    staticModules = [
+    ]
+    invalidModules=[]
+    for module in modules:
+        if module not in validModules :
+            invalidModules.append(module)
+    if invalidModules :
+        raise Exception("Modules %s are not Qt6 modules. Valid Qt6 modules are: %s"% (
+            str(invalidModules),str(validModules)))
+
+    moduleDefines = {
+        'QtScript'   : ['QT_SCRIPT_LIB'],
+        'QtSvg'      : ['QT_SVG_LIB'],
+        'QtSql'      : ['QT_SQL_LIB'],
+        'QtXml'      : ['QT_XML_LIB'],
+        'QtOpenGL'   : ['QT_OPENGL_LIB'],
+        'QtGui'      : ['QT_GUI_LIB'],
+        'QtNetwork'  : ['QT_NETWORK_LIB'],
+        'QtCore'     : ['QT_CORE_LIB'],
+        'QtWidgets'  : ['QT_WIDGETS_LIB'],
+    }
+    for module in modules :
+        try : self.AppendUnique(CPPDEFINES=moduleDefines[module])
+        except: pass
+    debugSuffix = ''
+    if sys.platform in [] and not crosscompiling :
+        if debug : debugSuffix = '_debug'
+        for module in modules :
+            if module not in pclessModules : continue
+            self.AppendUnique(LIBS=[module.replace('Qt','Qt6')+debugSuffix])
+            self.AppendUnique(LIBPATH=[os.path.join("$QT6DIR","lib")])
+            self.AppendUnique(CPPPATH=[os.path.join("$QT6DIR","include")])
+            self.AppendUnique(CPPPATH=[os.path.join("$QT6DIR","include",module)])
+        pcmodules = [module.replace('Qt','Qt6')+debugSuffix for module in modules if module not in pclessModules ]
+        if 'Qt6DBus' in pcmodules:
+            self.AppendUnique(CPPPATH=[os.path.join("$QT6DIR","include","Qt6DBus")])
+        if "Qt6Assistant" in pcmodules:
+            self.AppendUnique(CPPPATH=[os.path.join("$QT6DIR","include","Qt6Assistant")])
+            pcmodules.remove("Qt6Assistant")
+            pcmodules.append("Qt6AssistantClient")
+        self.AppendUnique(RPATH=[os.path.join("$QT6DIR","lib")])
+        self.ParseConfig('pkg-config %s --libs --cflags'% ' '.join(pcmodules))
+        self["QT6_MOCCPPPATH"] = self["CPPPATH"]
+        return
+    if sys.platform in ["win32", "darwin", "linux2", "linux"] or crosscompiling :
+        if crosscompiling:
+            transformedQtdir = transformToWinePath(self['QT6DIR'])
+            self['QT6_MOC'] = "QT6DIR=%s %s"%( transformedQtdir, self['QT6_MOC'])
+        self.AppendUnique(CPPPATH=[os.path.join("$QT6DIR","include")])
+        try: modules.remove("QtDBus")
+        except: pass
+        if debug : debugSuffix = 'd'
+        if "QtAssistant" in modules:
+            self.AppendUnique(CPPPATH=[os.path.join("$QT6DIR","include","QtAssistant")])
+            modules.remove("QtAssistant")
+            modules.append("QtAssistantClient")
+        #if sys.platform == "win32": self.AppendUnique(LIBS=['qtmain'+debugSuffix])
+        self.AppendUnique(LIBS=[lib.replace("Qt","Qt6")+debugSuffix for lib in modules if lib not in staticModules])
+        self.PrependUnique(LIBS=[lib+debugSuffix for lib in modules if lib in staticModules])
+        if 'QtOpenGL' in modules:
+            self.AppendUnique(LIBS=['opengl32'])
+        self.AppendUnique(CPPPATH=[ '$QT6DIR/include/'])
+        self.AppendUnique(CPPPATH=[ '$QT6DIR/include/'+module for module in modules])
+        if crosscompiling :
+            self["QT6_MOCCPPPATH"] = [
+                path.replace('$QT6DIR', transformedQtdir)
+                    for path in self['CPPPATH'] ]
+        else :
+            self["QT6_MOCCPPPATH"] = self["CPPPATH"]
+        self.AppendUnique(LIBPATH=[os.path.join('$QT6DIR','lib')])
+        if int(getattr(self, "QT6_DEBUG", 0)) == 1:
+            print("CPPPATH=%s" % self.subst("$CPPPATH"))
+            print("LIBPATH=%s" % self.subst("$LIBPATH"))
+            print("LIBS=%s" % self.subst("$LIBS"))
+        return
+        
+    """
+    if sys.platform=="darwin" :
+        # TODO: Test debug version on Mac
+        self.AppendUnique(LIBPATH=[os.path.join('$QT6DIR','lib')])
+        self.AppendUnique(LINKFLAGS="-F$QT6DIR/lib")
+        self.AppendUnique(LINKFLAGS="-L$QT6DIR/lib") #TODO clean!
+        if debug : debugSuffix = 'd'
+        for module in modules :
+#            self.AppendUnique(CPPPATH=[os.path.join("$QT6DIR","include")])
+#            self.AppendUnique(CPPPATH=[os.path.join("$QT6DIR","include",module)])
+# port qt6-mac:
+            self.AppendUnique(CPPPATH=[os.path.join("$QT6DIR","include", "qt6")])
+            self.AppendUnique(CPPPATH=[os.path.join("$QT6DIR","include", "qt6", module)])
+            if module in staticModules :
+                self.AppendUnique(LIBS=[module+debugSuffix]) # TODO: Add the debug suffix
+                self.AppendUnique(LIBPATH=[os.path.join("$QT6DIR","lib")])
+            else :
+#                self.Append(LINKFLAGS=['-framework', module])
+# port qt6-mac:
+                self.Append(LIBS=module)
+        if 'QtOpenGL' in modules:
+            self.AppendUnique(LINKFLAGS="-F/System/Library/Frameworks")
+            self.Append(LINKFLAGS=['-framework', 'AGL']) #TODO ughly kludge to avoid quotes
+            self.Append(LINKFLAGS=['-framework', 'OpenGL'])
+        self["QT6_MOCCPPPATH"] = self["CPPPATH"]
+        return
+# This should work for mac but doesn't
+#    env.AppendUnique(FRAMEWORKPATH=[os.path.join(env['QT6DIR'],'lib')])
+#    env.AppendUnique(FRAMEWORKS=['QtCore','QtGui','QtOpenGL', 'AGL'])
+    """
+        
+def exists(env):
+    return _detect(env)
diff --git a/workspace/sconstools3/qt6/docs/SConstruct b/workspace/sconstools3/qt6/docs/SConstruct
new file mode 100644
index 0000000..e4afdf7
--- /dev/null
+++ b/workspace/sconstools3/qt6/docs/SConstruct
@@ -0,0 +1,34 @@
+#
+# Copyright (c) 2001-2010,2011,2012 The SCons Foundation
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+
+import os
+
+env = Environment(ENV = os.environ,
+                  tools = ['docbook'])
+
+env.DocbookPdf('manual', DOCBOOK_XSL='pdf.xsl')
+env.DocbookPdf('reference', DOCBOOK_XSL='pdf.xsl')
+
+env.DocbookHtml('manual', DOCBOOK_XSL='html.xsl')
+env.DocbookHtml('reference', DOCBOOK_XSL='html.xsl')
+
diff --git a/workspace/sconstools3/qt6/docs/html.xsl b/workspace/sconstools3/qt6/docs/html.xsl
new file mode 100644
index 0000000..b4e9796
--- /dev/null
+++ b/workspace/sconstools3/qt6/docs/html.xsl
@@ -0,0 +1,55 @@
+<?xml version='1.0'?>
+<!--
+
+  Copyright (c) 2001-2010,2011,2012 The SCons Foundation
+
+  Permission is hereby granted, free of charge, to any person obtaining
+  a copy of this software and associated documentation files (the
+  "Software"), to deal in the Software without restriction, including
+  without limitation the rights to use, copy, modify, merge, publish,
+  distribute, sublicense, and/or sell copies of the Software, and to
+  permit persons to whom the Software is furnished to do so, subject to
+  the following conditions:
+
+  The above copyright notice and this permission notice shall be included
+  in all copies or substantial portions of the Software.
+
+  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+  KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+  WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+  NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+  LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+  OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+  WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+-->
+<xsl:stylesheet
+	xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
+	xmlns:fo="http://www.w3.org/1999/XSL/Format" 
+	version="1.0"> 
+
+	<xsl:import href="file:///usr/share/xml/docbook/stylesheet/docbook-xsl/html/docbook.xsl"/> 
+
+<xsl:param name="l10n.gentext.default.language" select="'en'"/>
+<xsl:param name="section.autolabel" select="1"/>
+<xsl:param name="html.stylesheet" select="'scons.css'"/>
+<xsl:param name="generate.toc">
+/appendix toc,title
+article/appendix  nop
+/article  toc,title
+book      toc,title,figure,table,example,equation
+/chapter  toc,title
+part      toc,title
+/preface  toc,title
+reference toc,title
+/sect1    toc
+/sect2    toc
+/sect3    toc
+/sect4    toc
+/sect5    toc
+/section  toc
+set       toc,title
+</xsl:param>
+
+</xsl:stylesheet> 
+
diff --git a/workspace/sconstools3/qt6/docs/manual.xml b/workspace/sconstools3/qt6/docs/manual.xml
new file mode 100644
index 0000000..490a410
--- /dev/null
+++ b/workspace/sconstools3/qt6/docs/manual.xml
@@ -0,0 +1,388 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+  Copyright (c) 2001-2010,2011,2012,2013 The SCons Foundation
+
+  Permission is hereby granted, free of charge, to any person obtaining
+  a copy of this software and associated documentation files (the
+  "Software"), to deal in the Software without restriction, including
+  without limitation the rights to use, copy, modify, merge, publish,
+  distribute, sublicense, and/or sell copies of the Software, and to
+  permit persons to whom the Software is furnished to do so, subject to
+  the following conditions:
+
+  The above copyright notice and this permission notice shall be included
+  in all copies or substantial portions of the Software.
+
+  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+  KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+  WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+  NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+  LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+  OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+  WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+-->
+<!DOCTYPE article PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
+"http://www.oasis-open.org/docbook/xml/4.1.2/docbookx.dtd">
+<article>
+  <title>The SCons qt5 tool</title>
+
+  <articleinfo>
+    <author>
+      <surname>Dirk Baechle</surname>
+    </author>
+
+    <pubdate>2012-12-13</pubdate>
+  </articleinfo>
+
+  <section id="basics">
+    <title>Basics</title>
+
+    <para>This tool can be used to compile Qt projects, designed for versions
+    5.x.y and higher. It is not usable for Qt3 and older versions, since some
+    of the helper tools (<literal>moc</literal>, <literal>uic</literal>)
+    behave different.</para>
+
+    <section id="install">
+      <title>Install</title>
+
+      <para>Installing it, requires you to copy (or, even better: checkout)
+      the contents of the package's <literal>qt5</literal> folder to</para>
+
+      <orderedlist>
+        <listitem>
+          <para><quote><literal>/path_to_your_project/site_scons/site_tools/qt5</literal></quote>,
+          if you need the Qt5 Tool in one project only, or</para>
+        </listitem>
+
+        <listitem>
+          <para><quote><literal>~/.scons/site_scons/site_tools/qt5</literal></quote>,
+          for a system-wide installation under your current login.</para>
+        </listitem>
+      </orderedlist>
+
+      <para>For more infos about this, please refer to</para>
+
+      <itemizedlist>
+        <listitem>
+          <para>the SCons User's Guide, sect. "Where to put your custom
+          Builders and Tools" and</para>
+        </listitem>
+
+        <listitem>
+          <para>the SCons Tools Wiki page at <ulink
+          url="http://scons.org/wiki/ToolsIndex">http://scons.org/wiki/ToolsIndex</ulink>.</para>
+        </listitem>
+      </itemizedlist>
+    </section>
+
+    <section id="activation">
+      <title>How to activate</title>
+
+      <para>For activating the tool "qt5", you have to add its name to the
+      Environment constructor, like this</para>
+
+      <screen>env = Environment(tools=['default','qt5'])
+</screen>
+
+      <para>On its startup, the Qt5 tool tries to read the variable
+      <literal>QT5DIR</literal> from the current Environment and
+      <literal>os.environ</literal>. If it is not set, the value of
+      <literal>QTDIR</literal> (in Environment/<literal>os.environ</literal>)
+      is used as a fallback.</para>
+
+      <para>So, you either have to explicitly give the path of your Qt5
+      installation to the Environment with</para>
+
+      <screen>env['QT5DIR'] = '/usr/local/Trolltech/Qt-5.2.3'
+</screen>
+
+      <para>or set the <literal>QT5DIR</literal> as environment variable in
+      your shell.</para>
+    </section>
+
+    <section id="requirements">
+      <title>Requirements</title>
+
+      <para>Under Linux, "qt5" uses the system tool
+      <literal>pkg-config</literal> for automatically setting the required
+      compile and link flags of the single Qt5 modules (like QtCore,
+      QtGui,...). This means that</para>
+
+      <orderedlist>
+        <listitem>
+          <para>you should have <literal>pkg-config</literal> installed,
+          and</para>
+        </listitem>
+
+        <listitem>
+          <para>you additionally have to set
+          <literal>PKG_CONFIG_PATH</literal> in your shell environment, such
+          that it points to $<literal>QT5DIR/lib/pkgconfig</literal> (or
+          $<literal>QT5DIR/lib</literal> for some older versions).</para>
+        </listitem>
+      </orderedlist>
+
+      <para>Based on these two environment variables
+      (<literal>QT5DIR</literal> and <literal>PKG_CONFIG_PATH</literal>), the
+      "qt5" tool initializes all <literal>QT5_*</literal> construction
+      variables listed in the Reference manual. This happens when the tool is
+      "detected" during Environment construction. As a consequence, the setup
+      of the tool gets a two-stage process, if you want to override the values
+      provided by your current shell settings:</para>
+
+      <screen># Stage 1: create plain environment
+qtEnv = Environment()
+# Set new vars
+qtEnv['QT5DIR'] = '/usr/local/Trolltech/Qt-5.2.3
+qtEnv['ENV']['PKG_CONFIG_PATH'] = '/usr/local/Trolltech/Qt-5.2.3/lib/pkgconfig'
+# Stage 2: add qt5 tool
+qtEnv.Tool('qt5')
+</screen>
+    </section>
+  </section>
+
+  <section id="boilerplate">
+    <title>Suggested boilerplate</title>
+
+    <para>Based on the requirements above, we suggest a simple ready-to-go
+    setup as follows:</para>
+
+    <para>SConstruct</para>
+
+    <screen># Detect Qt version
+qtdir = detectLatestQtDir()
+
+# Create base environment
+baseEnv = Environment()
+#...further customization of base env
+
+# Clone Qt environment
+qtEnv = baseEnv.Clone()
+# Set QT5DIR and PKG_CONFIG_PATH
+qtEnv['ENV']['PKG_CONFIG_PATH'] = os.path.join(qtdir, 'lib/pkgconfig')
+qtEnv['QT5DIR'] = qtdir
+# Add qt5 tool
+qtEnv.Tool('qt5')
+#...further customization of qt env
+
+# Export environments
+Export('baseEnv qtEnv')
+
+# Your other stuff...
+# ...including the call to your SConscripts
+</screen>
+
+    <para>In a SConscript</para>
+
+    <screen># Get the Qt5 environment
+Import('qtEnv')
+# Clone it
+env = qtEnv.clone()
+# Patch it
+env.Append(CCFLAGS=['-m32']) # or whatever
+# Use it
+env.StaticLibrary('foo', Glob('*.cpp'))
+</screen>
+
+    <para>The detection of the Qt directory could be as simple as directly
+    assigning a fixed path</para>
+
+    <screen>def detectLatestQtDir():
+    return "/usr/local/qt5.3.2"
+</screen>
+
+    <para>or a little more sophisticated</para>
+
+    <screen># Tries to detect the path to the installation of Qt with
+# the highest version number
+def detectLatestQtDir():
+    if sys.platform.startswith("linux"):
+        # Simple check: inspect only '/usr/local/Trolltech'
+        paths = glob.glob('/usr/local/Trolltech/*')
+        if len(paths):
+            paths.sort()
+            return paths[-1]
+        else:
+            return ""
+    else:
+        # Simple check: inspect only 'C:\Qt'
+        paths = glob.glob('C:\\Qt\\*')
+        if len(paths):
+            paths.sort()
+            return paths[-1]
+        else:
+            return os.environ.get("QTDIR","")
+</screen>
+  </section>
+
+  <section id="firstproject">
+    <title>A first project</title>
+
+    <para>The following SConscript is for a simple project with some cxx
+    files, using the QtCore, QtGui and QtNetwork modules:</para>
+
+    <screen>Import('qtEnv')
+env = qtEnv.Clone()
+env.EnableQt5Modules([
+                      'QtGui',
+                      'QtCore',
+                      'QtNetwork'
+                     ])
+# Add your CCFLAGS and CPPPATHs to env here...
+
+env.Program('foo', Glob('*.cpp')) 
+</screen>
+  </section>
+
+  <section id="mocup">
+    <title>MOC it up</title>
+
+    <para>For the basic support of automocing, nothing needs to be done by the
+    user. The tool usually detects the <literal>Q_OBJECT</literal> macro and
+    calls the <quote><literal>moc</literal></quote> executable
+    accordingly.</para>
+
+    <para>If you don't want this, you can switch off the automocing by
+    a</para>
+
+    <screen>env['QT5_AUTOSCAN'] = 0
+</screen>
+
+    <para>in your SConscript file. Then, you have to moc your files
+    explicitly, using the Moc5 builder.</para>
+
+    <para>You can also switch to an extended automoc strategy with</para>
+
+    <screen>env['QT5_AUTOSCAN_STRATEGY'] = 1
+</screen>
+
+    <para>Please read the description of the
+    <literal>QT5_AUTOSCAN_STRATEGY</literal> variable in the Reference manual
+    for details.</para>
+
+    <para>For debugging purposes, you can set the variable
+    <literal>QT5_DEBUG</literal> with</para>
+
+    <screen>env['QT5_DEBUG'] = 1
+</screen>
+
+    <para>which outputs a lot of messages during automocing.</para>
+  </section>
+
+  <section id="forms">
+    <title>Forms (.ui)</title>
+
+    <para>The header files with setup code for your GUI classes, are not
+    compiled automatically from your <literal>.ui</literal> files. You always
+    have to call the Uic5 builder explicitly like</para>
+
+    <screen>env.Uic5(Glob('*.ui'))
+env.Program('foo', Glob('*.cpp'))
+</screen>
+  </section>
+
+  <section id="resources">
+    <title>Resource files (.qrc)</title>
+
+    <para>Resource files are not built automatically, you always have to add
+    the names of the <literal>.qrc</literal> files to the source list for your
+    program or library:</para>
+
+    <screen>env.Program('foo', Glob('*.cpp')+Glob('*.qrc'))
+</screen>
+
+    <para>For each of the Resource input files, its prefix defines the name of
+    the resulting resource. An appropriate
+    <quote><literal>-name</literal></quote> option is added to the call of the
+    <literal>rcc</literal> executable by default.</para>
+
+    <para>You can also call the Qrc5 builder explicitly as</para>
+
+    <screen>qrccc = env.Qrc5('foo') # ['foo.qrc'] -&gt; ['qrc_foo.cc']
+</screen>
+
+    <para>or (overriding the default suffix)</para>
+
+    <screen>qrccc = env.Qrc5('myprefix_foo.cxx','foo.qrc') # -&gt; ['qrc_myprefix_foo.cxx']
+</screen>
+
+    <para>and then add the resulting cxx file to the sources of your
+    Program/Library:</para>
+
+    <screen>env.Program('foo', Glob('*.cpp') + qrccc)
+</screen>
+  </section>
+
+  <section id="translation">
+    <title>Translation files</title>
+
+    <para>The update of the <literal>.ts</literal> files and the conversion to
+    binary <literal>.qm</literal> files is not done automatically. You have to
+    call the corresponding builders on your own.</para>
+
+    <para>Example for updating a translation file:</para>
+
+    <screen>env.Ts5('foo.ts','.') # -&gt; ['foo.ts']
+</screen>
+
+    <para>By default, the <literal>.ts</literal> files are treated as
+    <emphasis>precious</emphasis> targets. This means that they are not
+    removed prior to a rebuild, but simply get updated. Additionally, they do
+    not get cleaned on a <quote><literal>scons -c</literal></quote>. If you
+    want to delete the translation files on the
+    <quote><literal>-c</literal></quote> SCons command, you can set the
+    variable <quote><literal>QT5_CLEAN_TS</literal></quote> like this</para>
+
+    <screen>env['QT5_CLEAN_TS']=1
+</screen>
+
+    <para>Example for releasing a translation file, i.e. compiling it to a
+    <literal>.qm</literal> binary file:</para>
+
+    <screen>env.Qm5('foo') # ['foo.ts'] -&gt; ['foo.qm']
+</screen>
+
+    <para>or (overriding the output prefix)</para>
+
+    <screen>env.Qm5('myprefix','foo') # ['foo.ts'] -&gt; ['myprefix.qm']
+</screen>
+
+    <para>As an extension both, the Ts5() and Qm5 builder, support the
+    definition of multiple targets. So, calling</para>
+
+    <screen>env.Ts5(['app_en','app_de'], Glob('*.cpp'))
+</screen>
+
+    <para>and</para>
+
+    <screen>env.Qm5(['app','copy'], Glob('*.ts'))
+</screen>
+
+    <para>should work fine.</para>
+
+    <para>Finally, two short notes about the support of directories for the
+    Ts5() builder. You can pass an arbitrary mix of cxx files and subdirs to
+    it, as in</para>
+
+    <screen>env.Ts5('app_en',['sub1','appwindow.cpp','main.cpp']))
+</screen>
+
+    <para>where <literal>sub1</literal> is a folder that gets scanned
+    recursively for cxx files by <literal>lupdate</literal>. But like this,
+    you lose all dependency information for the subdir, i.e. if a file inside
+    the folder changes, the .ts file is not updated automatically! In this
+    case you should tell SCons to always update the target:</para>
+
+    <screen>ts = env.Ts5('app_en',['sub1','appwindow.cpp','main.cpp'])
+env.AlwaysBuild(ts)
+</screen>
+
+    <para>Last note: specifying the current folder
+    <quote><literal>.</literal></quote> as input to Ts5() and storing the
+    resulting .ts file in the same directory, leads to a dependency cycle! You
+    then have to store the .ts and .qm files outside of the current folder, or
+    use <literal>Glob('*.cpp'))</literal> instead.</para>
+  </section>
+</article>
diff --git a/workspace/sconstools3/qt6/docs/pdf.xsl b/workspace/sconstools3/qt6/docs/pdf.xsl
new file mode 100644
index 0000000..c7495da
--- /dev/null
+++ b/workspace/sconstools3/qt6/docs/pdf.xsl
@@ -0,0 +1,62 @@
+<?xml version='1.0'?>
+<!--
+
+  Copyright (c) 2001-2010,2011,2012 The SCons Foundation
+
+  Permission is hereby granted, free of charge, to any person obtaining
+  a copy of this software and associated documentation files (the
+  "Software"), to deal in the Software without restriction, including
+  without limitation the rights to use, copy, modify, merge, publish,
+  distribute, sublicense, and/or sell copies of the Software, and to
+  permit persons to whom the Software is furnished to do so, subject to
+  the following conditions:
+
+  The above copyright notice and this permission notice shall be included
+  in all copies or substantial portions of the Software.
+
+  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+  KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+  WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+  NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+  LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+  OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+  WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+-->
+
+<xsl:stylesheet
+	xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
+	xmlns:fo="http://www.w3.org/1999/XSL/Format" 
+	version="1.0"> 
+
+	<xsl:import href="file:///usr/share/xml/docbook/stylesheet/docbook-xsl/fo/docbook.xsl"/> 
+
+<xsl:param name="l10n.gentext.default.language" select="'en'"/>
+<xsl:param name="section.autolabel" select="1"></xsl:param>
+<xsl:param name="toc.indent.width" select="0"></xsl:param>
+<xsl:param name="body.start.indent">0pt</xsl:param>
+<xsl:param name="shade.verbatim" select="1"></xsl:param>
+<xsl:param name="generate.toc">
+/appendix toc,title
+article/appendix  nop
+/article  toc,title
+book      toc,title,figure,table,example,equation
+/chapter  toc,title
+part      toc,title
+/preface  toc,title
+reference toc,title
+/sect1    toc
+/sect2    toc
+/sect3    toc
+/sect4    toc
+/sect5    toc
+/section  toc
+set       toc,title
+</xsl:param>
+
+<xsl:template match="varlistentry/term">
+	<xsl:call-template name="inline.boldseq"/>
+</xsl:template>
+
+</xsl:stylesheet> 
+
diff --git a/workspace/sconstools3/qt6/docs/qt5.xml b/workspace/sconstools3/qt6/docs/qt5.xml
new file mode 100644
index 0000000..d067cad
--- /dev/null
+++ b/workspace/sconstools3/qt6/docs/qt5.xml
@@ -0,0 +1,600 @@
+<!--
+  Copyright (c) 2001-2010,2011,2012 The SCons Foundation
+
+This file is processed by the bin/SConsDoc.py module.
+See its __doc__ string for a discussion of the format.
+-->
+<tool name="qt5">
+<summary>
+Sets construction variables for building Qt5 applications.
+</summary>
+<sets>
+QT5DIR
+QT5_BINPATH
+QT5_MOC
+QT5_UIC
+QT5_RCC
+QT5_AUTOSCAN
+QT5_AUTOSCAN_STRATEGY
+QT5_AUTOMOC_CPPPATH
+QT5_AUTOMOC_SCANCPPPATH
+QT5_UICFLAGS
+QT5_MOCFROMHFLAGS
+QT5_MOCFROMCXXFLAGS
+QT5_UICDECLPREFIX
+QT5_UICDECLSUFFIX
+QT5_MOCHPREFIX
+QT5_MOCHSUFFIX
+QT5_MOCCXXPREFIX
+QT5_MOCCXXSUFFIX
+QT5_MOCDEFPREFIX
+QT5_MOCDEFSUFFIX
+QT5_UISUFFIX
+QT5_UICCOM
+QT5_GOBBLECOMMENTS
+QT5_CPPDEFINES_PASSTOMOC
+QT5_CLEAN_TS
+QT5_DEBUG
+QT5_XMOCHPREFIX
+QT5_XMOCHSUFFIX
+QT5_XMOCCXXPREFIX
+QT5_XMOCCXXSUFFIX
+QT5_LUPDATE
+QT5_LRELEASE
+QT5_LUPDATEFLAGS
+QT5_LRELEASEFLAGS
+QT5_QRCFLAGS
+QT5_UICDECLPREFIX
+QT5_UICDECLSUFFIX
+QT5_MOCINCPREFIX
+QT5_QRCSUFFIX
+QT5_QRCCXXSUFFIX
+QT5_QRCCXXPREFIX
+QT5_MOCDEFINES
+QT5_MOCCPPPATH
+QT5_MOCINCFLAGS
+QT5_LUPDATECOM
+QT5_LRELEASECOM
+</sets>
+<uses>
+</uses>
+</tool>
+
+<builder name="Moc5">
+<summary>
+Builds an output file from a moc input file. Moc input files are either
+header files or cxx files. This builder is only available after using the
+tool 'qt5'. It should be your first choice when manually Mocing files
+and is the builder for the Q_OBJECT driven Automoc strategy (#0, the default).
+It can can be controlled via the QT5_MOC* variables. See the &cv-link-QT5DIR; and
+&cv-link-QT5_AUTOSCAN_STRATEGY; variables for more information.
+Example:
+
+<example>
+env.Moc5('foo.h') # generates moc_foo.cc
+env.Moc5('foo.cpp') # generates foo.moc
+</example>
+</summary>
+</builder>
+
+<builder name="XMoc5">
+<summary>
+Just like the &b-Moc5; builder, it builds an output file from a moc input file.
+Moc input files are either header files or cxx files. This builder is only available after using the
+tool 'qt5'. It is defined separately for the include driven Automoc strategy (#1)
+and can be controlled via the QT5_XMOC* variables. See the &cv-link-QT5DIR; and
+&cv-link-QT5_AUTOSCAN_STRATEGY; variables for more information.
+Example:
+
+<example>
+env.XMoc5('foo.h') # generates moc_foo.cpp
+env.XMoc5('foo.cpp') # generates foo.moc
+</example>
+</summary>
+</builder>
+
+<builder name="ExplicitMoc5">
+<summary>
+Just like the &b-Moc5; builder, it builds an output file from a moc input file.
+However, it does not use any default prefix or suffix for the filenames.
+You can, and have to, specify the full source and target names explicitly.
+This builder is only available after using the
+tool 'qt5'. It can be your last resort, when you have to moc single files
+from/to exotic filenames.
+Example:
+
+<example>
+env.ExplicitMoc5('moced_foo.cxx','foo.h') # generates moced_foo.cxx
+</example>
+</summary>
+</builder>
+
+
+<builder name="Uic5">
+<summary>
+Builds a header file from an .ui file, where the former
+contains the setup code for a GUI class.
+This builder is only available after using the tool 'qt5'.
+Using this builder lets you override the standard
+naming conventions (be careful: prefixes are always prepended to names of
+built files; if you don't want prefixes, you may set them to ``).
+See the &cv-link-QT5DIR; variable for more information.
+Example:
+
+<example>
+env.Uic5('foo.ui') # -> 'ui_foo.h'
+</example>
+</summary>
+</builder>
+
+<builder name="ExplicitUic5">
+<summary>
+Just like the &b-Uic5; builder, it builds a header file from a .ui input file.
+However, it does not use any default prefix or suffix for the filenames.
+You can, and have to, specify the full source and target names explicitly.
+This builder is only available after using the
+tool 'qt5'. It can be your last resort, when you have to convert .ui
+files to exotic filenames.
+Example:
+
+<example>
+env.ExplicitUic5('uiced_foo.hpp','foo.ui') # generates uiced_foo.hpp
+</example>
+</summary>
+</builder>
+
+<builder name="Qrc5">
+<summary>
+Builds a cxx file, containing all resources from the given
+.qrc file.
+This builder is only available after using the tool 'qt5'.
+
+Example:
+
+<example>
+env.Qrc5('foo.qrc') # -> ['qrc_foo.cc']
+</example>
+</summary>
+</builder>
+
+<builder name="Ts5">
+<summary>
+Scans the source files in the given path for tr() marked strings,
+that should get translated. Writes a .ts file for the Qt Linguist.
+This builder is only available after using the tool 'qt5'.
+
+Example:
+
+<example>
+env.Ts5('foo.ts','.') # -> ['foo.ts']
+</example>
+</summary>
+</builder>
+
+
+<builder name="Qm5">
+<summary>
+Compiles a given .ts file (Qt Linguist) into a binary .qm file.
+This builder is only available after using the tool 'qt5'.
+
+Example:
+
+<example>
+env.Qm5('foo.ts') # -> ['foo.qm']
+</example>
+</summary>
+</builder>
+
+
+<cvar name="QT5DIR">
+<summary>
+The Qt5 tool tries to read this from the current Environment and then from os.environ.
+If it is not set and found, the value of QTDIR (in the Environment and os.environ) is
+used as a fallback.
+It also initializes all QT5_*
+construction variables listed below.
+(Note that all paths are constructed
+with python's os.path.join() method,
+but are listed here with the '/' separator
+for easier reading.)
+In addition, the construction environment
+variables &cv-link-CPPPATH;,
+&cv-link-LIBPATH; and
+&cv-link-LIBS; may be modified
+and the variables
+PROGEMITTER, SHLIBEMITTER and LIBEMITTER
+are modified. Because the build-performance is affected when using this tool,
+you have to explicitly specify it at Environment creation:
+
+<example>
+Environment(tools=['default','qt5'])
+</example>
+
+The Qt5 tool supports the following operations:
+
+<emphasis Role="strong">Automatic moc file generation from header files.</emphasis>
+You do not have to specify moc files explicitly, the tool does it for you.
+However, there are a few preconditions to do so: Your header file must have
+the same filebase as your implementation file and must stay in the same
+directory. It must have one of the suffixes .h, .hpp, .H, .hxx, .hh. You
+can turn off automatic moc file generation by setting &cv-link-QT5_AUTOSCAN; to 0.
+See also the corresponding builder method
+&b-Moc5;().
+
+<emphasis Role="strong">Automatic moc file generation from cxx files.</emphasis>
+As stated in the Qt documentation, include the moc file at the end of
+the cxx file. Note that you have to include the file, which is generated
+by the transformation ${QT5_MOCCXXPREFIX}&lt;basename&gt;${QT5_MOCCXXSUFFIX}, by default
+&lt;basename&gt;.moc. A warning is generated after building the moc file, if you
+do not include the correct file. If you are using VariantDir, you may
+need to specify duplicate=1. You can turn off automatic moc file generation
+by setting &cv-link-QT5_AUTOSCAN; to 0. See also the corresponding
+&b-Moc5;
+builder method.
+
+<emphasis Role="strong">Handling of .ui files.</emphasis>
+TODO: describe a little
+See also the corresponding
+&b-Uic5;
+builder method.
+
+<emphasis Role="strong">Handling translation files (.ts and .qm).</emphasis>
+TODO: describe a little
+See also the corresponding
+builder methods &b-Ts5; and &b-Qm5;.
+
+<emphasis Role="strong">Compiling resource files (.qrc).</emphasis>
+TODO: describe a little
+See also the corresponding
+&b-Qrc5; builder method.
+
+</summary>
+</cvar>
+
+<cvar name="QT5_AUTOSCAN">
+<summary>
+The default is '1', which means that the tool is automatically 
+scanning for mocable files (see also &cv-link-QT5_AUTOSCAN_STRATEGY;).
+You can set this variable to '0' to
+switch it off, and then use the &b-Moc5; Builder to explicitly
+specify files to run moc on.
+</summary>
+</cvar>
+
+<cvar name="QT5_BINPATH">
+<summary>
+The path where the Qt5 binaries are installed.
+The default value is '&cv-link-QT5DIR;/bin'.
+</summary>
+</cvar>
+
+<cvar name="QT5_MOCCPPPATH">
+<summary>
+The path where the Qt5 header files are installed.
+The default value is '&cv-link-QT5DIR;/include'.
+Note: If you set this variable to None,
+the tool won't change the &cv-link-CPPPATH;
+construction variable.
+</summary>
+</cvar>
+
+<cvar name="QT5_DEBUG">
+<summary>
+Prints lots of debugging information while scanning for moc files.
+</summary>
+</cvar>
+
+<cvar name="QT5_MOC">
+<summary>
+The path to the Qt5 moc executable.
+Default value is '&cv-link-QT5_BINPATH;/moc'.
+</summary>
+</cvar>
+
+<cvar name="QT5_MOCCXXPREFIX">
+<summary>
+Default value is ''. Prefix for moc output files, when source is a cxx file and the 
+Automoc strategy #0 (Q_OBJECT driven) is used.
+</summary>
+</cvar>
+
+<cvar name="QT5_MOCCXXSUFFIX">
+<summary>
+Default value is '.moc'. Suffix for moc output files, when source is a cxx file and the
+Automoc strategy #0 (Q_OBJECT driven) is used.
+</summary>
+</cvar>
+
+<cvar name="QT5_MOCFROMCXXFLAGS">
+<summary>
+Default value is '-i'. These flags are passed to moc, when moccing a
+C++ file.
+</summary>
+</cvar>
+
+<cvar name="QT5_MOCFROMHFLAGS">
+<summary>
+Default value is ''. These flags are passed to moc, when moccing a header
+file.
+</summary>
+</cvar>
+
+<cvar name="QT5_MOCHPREFIX">
+<summary>
+Default value is 'moc_'. Prefix for moc output files, when the source file is a header
+and the Automoc strategy #0 (Q_OBJECT driven) is used.
+</summary>
+</cvar>
+
+<cvar name="QT5_MOCHSUFFIX">
+<summary>
+Default value is '&cv-link-CXXFILESUFFIX;'. Suffix for moc output files, when 
+the source file is a header and the Automoc strategy #0 (Q_OBJECT driven) is used.
+</summary>
+</cvar>
+
+<cvar name="QT5_UIC">
+<summary>
+Default value is '&cv-link-QT5_BINPATH;/uic'. The path to the Qt5 uic executable.
+</summary>
+</cvar>
+
+<cvar name="QT5_UICCOM">
+<summary>
+Command to generate the required header and source files from .ui form files.
+Is compiled from &cv-link-QT5_UIC; and &cv-link-QT5_UICFLAGS;.
+</summary>
+</cvar>
+
+<cvar name="QT5_UICCOMSTR">
+<summary>
+The string displayed when generating header and source files from .ui form files.
+If this is not set, then &cv-link-QT5_UICCOM; (the command line) is displayed.
+</summary>
+</cvar>
+
+<cvar name="QT5_UICFLAGS">
+<summary>
+Default value is ''. These flags are passed to the Qt5 uic executable, when creating header
+and source files from a .ui form file.
+</summary>
+</cvar>
+
+<cvar name="QT5_UICDECLPREFIX">
+<summary>
+Default value is 'ui_'. Prefix for uic generated header files.
+</summary>
+</cvar>
+
+<cvar name="QT5_UICDECLSUFFIX">
+<summary>
+Default value is '.h'. Suffix for uic generated header files.
+</summary>
+</cvar>
+
+<cvar name="QT5_UISUFFIX">
+<summary>
+Default value is '.ui'. Suffix of designer input files (form files) in Qt5.
+</summary>
+</cvar>
+
+<cvar name="QT5_AUTOSCAN_STRATEGY">
+<summary>
+Default value is '0'. When using the Automoc feature of the Qt5 tool, you
+can select different strategies for detecting which files should get moced.
+The simple approach ('0' as the default) scans header and source files for
+the Q_OBJECT macro, so the trigger 'moc or not' is Q_OBJECT driven. If it is
+found, the corresponding file gets moced with
+the &b-Moc5; builder. This results in the files 'moc_foo.cc' and 'foo.moc' for header
+and source file, respectively. They get added to the list of sources, for compiling
+the current library or program.
+
+In older Qt manuals, a different technique for mocing is recommended. A cxx file
+includes the moced output of itself or its header at the end. This approach is somewhat
+deprecated, but the 'qtsolutions' by Qt are still based on it, for example. You also
+might have to switch older Qt sources to a new version 5.x.y. Then you can set
+this variable to '1', for 'include driven' mocing. This means that the tool searches
+for '#include' statements in all cxx files, containing a file pattern 'moc_foo.cpp'
+and 'foo.moc' for header and source file, respectively. If the file 'foo.h/foo.cpp'
+then contains a Q_OBJECT macro, it gets moced but is NOT added to the list of sources.
+This is the important difference between the two strategies. If no matching
+include patterns are found for the current cxx file, the Q_OBJECT driven method (#0)
+is tried as fallback.
+</summary>
+</cvar>
+
+<cvar name="QT5_AUTOMOC_SCANCPPPATH">
+<summary>
+The default is '1', meaning that the tool scans 
+for mocable files not only in the current directory, but
+also in all CPPPATH folders (see also &cv-link-QT5_AUTOMOC_CPPPATH;).
+You can set this variable to '0' to
+switch it off on rare occasions, e.g. when too many search
+folders give you a bad performance in large projects.
+</summary>
+</cvar>
+
+<cvar name="QT5_AUTOMOC_CPPPATH">
+<summary>
+The list of paths to scan for mocable files
+(see also &cv-link-QT5_AUTOMOC_SCANCPPPATH;), it is empty by default
+which means that the CPPPATH variable is used.
+You can set this variable to a subset of CPPPATH in order
+to improve performance, i.e. to minimize the search space.
+</summary>
+</cvar>
+
+<cvar name="QT5_GOBBLECOMMENTS">
+<summary>
+Default value is '0' (disabled). When you set this variable to '1',
+you enable the automatic removal of C/C++ comments, while searching for
+the Q_OBJECT keyword during the Automoc process. This can be helpful if you
+have the string Q_OBJECT in one of your comments, but don't want this
+file to get moced.
+</summary>
+</cvar>
+
+<cvar name="QT5_CPPDEFINES_PASSTOMOC">
+<summary>
+Default value is '1' (enabled). When you set this variable to '1',
+all currently set CPPDEFINES get passed to the moc executable. It does not matter
+which strategy you selected with &cv-link-QT5_AUTOSCAN_STRATEGY; or whether you
+call the &b-Moc5; builder directly.
+</summary>
+</cvar>
+
+<cvar name="QT5_CLEAN_TS">
+<summary>
+Default value is '0' (disabled). When you set this variable to '1',
+the &b-Ts5; builder will delete your .ts files on a 'scons -c'. Normally,
+these files for the QtLinguist are treated as 'precious' (they are not removed
+prior to a rebuild) and do not get cleaned.
+</summary>
+</cvar>
+
+<cvar name="QT5_XMOCHPREFIX">
+<summary>
+Default value is 'moc_'. 
+Like &cv-link-QT5_MOCHPREFIX;, this is the prefix for moc output files, when
+the source file is a header
+and the Automoc strategy #1 (include driven) is used.
+</summary>
+</cvar>
+
+<cvar name="QT5_XMOCHSUFFIX">
+<summary>
+Default value is '.cpp'. 
+Like &cv-link-QT5_MOCHSUFFIX;, this is the suffix for moc output files, when 
+the source file is a header and the Automoc strategy #1
+(include driven) is used.
+</summary>
+</cvar>
+
+<cvar name="QT5_XMOCCXXPREFIX">
+<summary>
+Default value is ''. 
+Like &cv-link-QT5_MOCCXXPREFIX;, this is the
+prefix for moc output files, when source is a cxx file and the 
+Automoc strategy #1 (include driven) is used.
+</summary>
+</cvar>
+
+<cvar name="QT5_XMOCCXXSUFFIX">
+<summary>
+Default value is '.moc'. 
+Like &cv-link-QT5_MOCCXXSUFFIX;, this is the 
+suffix for moc output files, when source is a cxx file and the
+Automoc strategy #1 (include driven) is used.
+</summary>
+</cvar>
+
+<cvar name="QT5_RCC">
+<summary>
+Default value is '&cv-link-QT5_BINPATH;/rcc'. The path to the Qt5 rcc
+executable (resource file compiler).
+</summary>
+</cvar>
+
+<cvar name="QT5_LUPDATE">
+<summary>
+Default value is '&cv-link-QT5_BINPATH;/lupdate'. The path to the Qt5 lupdate
+executable (updates the .ts files from sources).
+</summary>
+</cvar>
+
+<cvar name="QT5_LRELEASE">
+<summary>
+Default value is '&cv-link-QT5_BINPATH;/lrelease'. The path to the Qt5 lrelease
+executable (converts .ts files to binary .qm files).
+</summary>
+</cvar>
+
+<cvar name="QT5_QRCFLAGS">
+<summary>
+Default value is ''. These flags are passed to the Qt5 rcc executable,
+when compiling a resource file.
+</summary>
+</cvar>
+
+<cvar name="QT5_LUPDATEFLAGS">
+<summary>
+Default value is ''. These flags are passed to the Qt5 lupdate executable,
+when updating .ts files from sources.
+</summary>
+</cvar>
+
+<cvar name="QT5_LRELEASEFLAGS">
+<summary>
+Default value is ''. These flags are passed to the Qt5 lrelease executable,
+when compiling .ts files into binary .qm files.
+</summary>
+</cvar>
+
+<cvar name="QT5_MOCINCPREFIX">
+<summary>
+Default value is '-I'. The prefix for specifying include directories to the
+Qt5 moc executable.
+</summary>
+</cvar>
+
+<cvar name="QT5_QRCSUFFIX">
+<summary>
+Default value is '.qrc'. Suffix of Qt5 resource files.
+</summary>
+</cvar>
+
+<cvar name="QT5_QRCCXXSUFFIX">
+<summary>
+Default value is '$CXXFILESUFFIX'. 
+This is the suffix for compiled .qrc resource files.
+</summary>
+</cvar>
+
+<cvar name="QT5_QRCCXXPREFIX">
+<summary>
+Default value is 'qrc_'.
+This is the prefix for compiled .qrc resource files.
+</summary>
+</cvar>
+
+<cvar name="QT5_MOCINCFLAGS">
+<summary>
+List of include paths for the Qt5 moc executable, is compiled from
+&cv-link-QT5_MOCINCPREFIX;, &cv-link-QT5_MOCCPPPATH; and &cv-link-INCSUFFIX;.
+</summary>
+</cvar>
+
+<cvar name="QT5_MOCDEFINES">
+<summary>
+List of CPP defines that are passed to the Qt5 moc executable,
+is compiled from &cv-link-QT5_MOCDEFPREFIX;, &cv-link-CPPDEFINES; and &cv-link-QT5_MOCDEFSUFFIX;.
+</summary>
+</cvar>
+
+<cvar name="QT5_LUPDATECOM">
+<summary>
+Command to update .ts files for translation from the sources.
+</summary>
+</cvar>
+
+<cvar name="QT5_LUPDATECOMSTR">
+<summary>
+The string displayed when updating .ts files from the sources.
+If this is not set, then &cv-link-QT5_LUPDATECOM; (the command line) is displayed.
+</summary>
+</cvar>
+
+<cvar name="QT5_LRELEASECOM">
+<summary>
+Command to convert .ts files to binary .qm files.
+</summary>
+</cvar>
+
+<cvar name="QT5_LRELEASECOMSTR">
+<summary>
+The string displayed when converting .ts files to binary .qm files.
+If this is not set, then &cv-link-QT5_RCC; (the command line) is displayed.
+</summary>
+</cvar>
+
+
diff --git a/workspace/sconstools3/qt6/docs/reference.xml b/workspace/sconstools3/qt6/docs/reference.xml
new file mode 100644
index 0000000..a9b459a
--- /dev/null
+++ b/workspace/sconstools3/qt6/docs/reference.xml
@@ -0,0 +1,717 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+  Copyright (c) 2001-2010,2011,2012 The SCons Foundation
+
+  Permission is hereby granted, free of charge, to any person obtaining
+  a copy of this software and associated documentation files (the
+  "Software"), to deal in the Software without restriction, including
+  without limitation the rights to use, copy, modify, merge, publish,
+  distribute, sublicense, and/or sell copies of the Software, and to
+  permit persons to whom the Software is furnished to do so, subject to
+  the following conditions:
+
+  The above copyright notice and this permission notice shall be included
+  in all copies or substantial portions of the Software.
+
+  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+  KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+  WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+  NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+  LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+  OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+  WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+-->
+<!DOCTYPE article PUBLIC "-//OASIS//DTD DocBook XML V4.3//EN"
+"http://www.oasis-open.org/docbook/xml/4.3/docbookx.dtd">
+<article>
+  <articleinfo>
+    <title>SCons tool <quote>qt5</quote> - Reference</title>
+
+    <author>
+      <firstname>Dirk</firstname>
+
+      <surname>Baechle</surname>
+    </author>
+
+    <pubdate>2012-12-13</pubdate>
+  </articleinfo>
+
+  <abstract>
+    <para>This reference lists all the variables that are used within the
+    <quote>qt5</quote> tool, and the available builders. It is intended for
+    SCons tool developers and core programmers, as a normal user you should
+    read the manual instead.</para>
+  </abstract>
+
+  <section>
+    <title>What it does</title>
+
+    <para>The <quote>qt5</quote> tool sets construction variables and
+    registers builders for creating applications and libraries that use the
+    Qt5 framework by Trolltech/Nokia.</para>
+
+    <para>It supports the following operations:</para>
+
+    <section>
+      <title>Automatic moc file generation from header files</title>
+
+      <para>You do not have to specify moc files explicitly, the tool does it
+      for you. However, there are a few preconditions to do so: Your header
+      file must have the same filebase as your implementation file. It must
+      have one of the suffixes <literal>.h</literal>, <literal>.hpp</literal>,
+      <literal>.H</literal>, <literal>.hxx</literal>, <literal>.hh</literal>.
+      You can turn off automatic moc file generation by setting
+      <literal>QT5_AUTOSCAN</literal> to 0. See also the corresponding builder
+      method <literal>Moc5()</literal>.</para>
+    </section>
+
+    <section>
+      <title>Automatic moc file generation from cxx files</title>
+
+      <para>As stated in the Qt documentation, include the moc file at the end
+      of the cxx file. Note that you have to include the file, which is
+      generated by the transformation</para>
+
+      <para><screen>${QT5_MOCCXXPREFIX}&lt;basename&gt;${QT5_MOCCXXSUFFIX}</screen>,
+      by default <literal>&lt;basename&gt;.moc</literal>. A warning is
+      generated after building the moc file, if you do not include the correct
+      file. If you are using VariantDir, you may need to specify
+      <literal>duplicate=1</literal>. You can turn off automatic moc file
+      generation by setting <literal>QT5_AUTOSCAN</literal> to 0. See also the
+      corresponding <literal>Moc5</literal> builder method.</para>
+    </section>
+
+    <section>
+      <title>Handling of .ui files</title>
+
+      <para>TODO: describe in a little more detail.</para>
+
+      <para>See also the corresponding <literal>Uic5</literal> builder
+      method.</para>
+    </section>
+
+    <section>
+      <title>Handling translation files (.ts and .qm)</title>
+
+      <para>TODO: describe in a little more detail.</para>
+
+      <para>See also the corresponding builder methods Ts5 and Qm5.</para>
+    </section>
+
+    <section>
+      <title>Compiling resource files (.qrc)</title>
+
+      <para>TODO: describe in a little more detail.</para>
+
+      <para>See also the corresponding Qrc5 builder method.</para>
+    </section>
+  </section>
+
+  <section>
+    <title>Builders</title>
+
+    <para></para>
+
+    <section>
+      <title>Moc5</title>
+
+      <para>Builds an output file from a moc input file. Moc input files are
+      either header files or cxx files. This builder is only available after
+      using the tool 'qt5'.</para>
+
+      <para>Example:</para>
+
+      <para><screen>env.Moc5('foo.h') # generates moc_foo.cc
+env.Moc5('foo.cpp') # generates foo.moc
+</screen></para>
+    </section>
+
+    <section>
+      <title>XMoc5</title>
+
+      <para>Just like the Moc5 builder, it builds an output file from a moc
+      input file. Moc input files are either header files or cxx files. This
+      builder is only available after using the tool 'qt5'. It is defined
+      separately for the include driven Automoc strategy (#1) and can be
+      controlled via the QT5_XMOC* variables.</para>
+
+      <para>Example:</para>
+
+      <para><screen>env.XMoc5('foo.h') # generates moc_foo.cpp
+env.XMoc5('foo.cpp') # generates foo.moc
+</screen></para>
+    </section>
+
+    <section>
+      <title>ExplicitMoc5</title>
+
+      <para>Just like the <literal>Moc5</literal> builder, it builds an output
+      file from a moc input file. However, it does not use any default prefix
+      or suffix for the filenames. You can, and have to, specify the full
+      source and target names explicitly. This builder is only available after
+      using the tool 'qt5'. It can be your last resort, when you have to moc
+      single files from/to exotic filenames.</para>
+
+      <para>Example:</para>
+
+      <para><screen>env.ExplicitMoc5('moced_foo.cxx','foo.h') # generates moced_foo.cxx
+</screen></para>
+    </section>
+
+    <section>
+      <title>Uic5</title>
+
+      <para>Builds a header file from an .ui file, where the former contains
+      the setup code for a GUI class. This builder is only available after
+      using the tool 'qt5'. Using this builder lets you override the standard
+      naming conventions (be careful: prefixes are always prepended to names
+      of built files; if you don't want prefixes, you may set them to
+      ``).</para>
+
+      <para>Example:</para>
+
+      <para><screen>env.Uic5('foo.ui') # -&gt; 'ui_foo.h'
+</screen></para>
+    </section>
+
+    <section>
+      <title>ExplicitUic5</title>
+
+      <para>Just like the <literal>Uic5</literal> builder, it builds a header
+      file from a .ui input file. However, it does not use any default prefix
+      or suffix for the filenames. You can, and have to, specify the full
+      source and target names explicitly. This builder is only available after
+      using the tool 'qt5'. It can be your last resort, when you have to
+      convert .ui files to exotic filenames.</para>
+
+      <para>Example:</para>
+
+      <para><screen>env.ExplicitUic5('uiced_foo.hpp','foo.ui') # generates uiced_foo.hpp
+</screen></para>
+    </section>
+
+    <section>
+      <title>Qrc5</title>
+
+      <para>Builds a cxx file, containing all resources from the given
+      <literal>.qrc</literal> file. This builder is only available after using
+      the tool 'qt5'.</para>
+
+      <para>Example:</para>
+
+      <para><screen>env.Qrc5('foo.qrc') # -&gt; ['qrc_foo.cc']
+</screen></para>
+    </section>
+
+    <section>
+      <title>Ts5</title>
+
+      <para>Scans the source files in the given path for tr() marked strings,
+      that should get translated. Writes a <literal>.ts</literal> file for the
+      Qt Linguist. This builder is only available after using the tool
+      'qt5'.</para>
+
+      <para>Example:</para>
+
+      <para><screen>env.Ts5('foo.ts','.') # -&gt; ['foo.ts']
+</screen></para>
+    </section>
+
+    <section>
+      <title>Qm5</title>
+
+      <para>Compiles a given <literal>.ts</literal> file (Qt Linguist) into a
+      binary <literal>.qm</literal> file. This builder is only available after
+      using the tool 'qt5'.</para>
+
+      <para>Example:</para>
+
+      <para><screen>env.Qm5('foo.ts') # -&gt; ['foo.qm']
+</screen></para>
+    </section>
+  </section>
+
+  <section>
+    <title>Variables</title>
+
+    <para><variablelist>
+        <varlistentry>
+          <term>QT5DIR</term>
+
+          <listitem>
+            <para>The Qt5 tool tries to read this from the current Environment
+            and <literal>os.environ</literal>. If it is not set and found, the
+            value of QTDIR (in Environment/<literal>os.environ</literal>) is
+            used as a fallback. It is used to initialize all QT5_*
+            construction variables listed below.</para>
+          </listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term>QT5_AUTOSCAN</term>
+
+          <listitem>
+            <para>The default is '1', which means that the tool is
+            automatically scanning for mocable files (see also
+            <literal>QT5_AUTOSCAN_STRATEGY</literal>). You can set this
+            variable to '0' to switch it off, and then use the
+            <literal>Moc5</literal> Builder to explicitly specify files to run
+            moc on.</para>
+          </listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term>QT5_BINPATH</term>
+
+          <listitem>
+            <para>The path where the Qt5 binaries are installed. The default
+            value is '<literal>QT5DIR</literal>/bin'.</para>
+          </listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term>QT5_MOCCPPPATH</term>
+
+          <listitem>
+            <para>The path where the Qt5 header files are installed. The
+            default value is '<literal>QT5DIR</literal>/include'. Note: If you
+            set this variable to None, the tool won't change the
+            <literal>CPPPATH</literal> construction variable.</para>
+          </listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term>QT5_DEBUG</term>
+
+          <listitem>
+            <para>Prints lots of debugging information while scanning for moc
+            files.</para>
+          </listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term>QT5_MOC</term>
+
+          <listitem>
+            <para>The path to the Qt5 moc executable. Default value is
+            '<literal>QT5_BINPATH</literal>/moc'.</para>
+          </listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term>QT5_MOCCXXPREFIX</term>
+
+          <listitem>
+            <para>Default value is ''. Prefix for moc output files, when
+            source is a cxx file and the Automoc strategy #0 (Q_OBJECT driven)
+            is used.</para>
+          </listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term>QT5_MOCCXXSUFFIX</term>
+
+          <listitem>
+            <para>Default value is '.moc'. Suffix for moc output files, when
+            source is a cxx file and the Automoc strategy #0 (Q_OBJECT driven)
+            is used.</para>
+          </listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term>QT5_MOCFROMCXXFLAGS</term>
+
+          <listitem>
+            <para>Default value is '-i'. These flags are passed to moc, when
+            moccing a C++ file.</para>
+          </listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term>QT5_MOCFROMHFLAGS</term>
+
+          <listitem>
+            <para>Default value is ''. These flags are passed to moc, when
+            moccing a header file.</para>
+          </listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term>QT5_MOCHPREFIX</term>
+
+          <listitem>
+            <para>Default value is 'moc_'. Prefix for moc output files, when
+            the source file is a header and the Automoc strategy #0 (Q_OBJECT
+            driven) is used.</para>
+          </listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term>QT5_MOCHSUFFIX</term>
+
+          <listitem>
+            <para>Default value is '<literal>CXXFILESUFFIX</literal>'. Suffix
+            for moc output files, when the source file is a header and the
+            Automoc strategy #0 (Q_OBJECT driven) is used.</para>
+          </listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term>QT5_UIC</term>
+
+          <listitem>
+            <para>Default value is '<literal>QT5_BINPATH</literal>/uic'. The
+            path to the Qt5 uic executable.</para>
+          </listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term>QT5_UICCOM</term>
+
+          <listitem>
+            <para>Command to generate the required header and source files
+            from .ui form files. Is compiled from <literal>QT5_UIC</literal>
+            and <literal>QT5_UICFLAGS</literal>.</para>
+          </listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term>QT5_UICCOMSTR</term>
+
+          <listitem>
+            <para>The string displayed when generating header and source files
+            from .ui form files. If this is not set, then
+            <literal>QT5_UICCOM</literal> (the command line) is
+            displayed.</para>
+          </listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term>QT5_UICFLAGS</term>
+
+          <listitem>
+            <para>Default value is ''. These flags are passed to the Qt5 uic
+            executable, when creating header and source files from a .ui form
+            file.</para>
+          </listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term>QT5_UICDECLPREFIX</term>
+
+          <listitem>
+            <para>Default value is 'ui_'. Prefix for uic generated header
+            files.</para>
+          </listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term>QT5_UICDECLSUFFIX</term>
+
+          <listitem>
+            <para>Default value is '.h'. Suffix for uic generated header
+            files.</para>
+          </listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term>QT5_UISUFFIX</term>
+
+          <listitem>
+            <para>Default value is '.ui'. Suffix of designer input files (form
+            files) in Qt5.</para>
+          </listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term>QT5_AUTOSCAN_STRATEGY</term>
+
+          <listitem>
+            <para>Default value is '0'. When using the Automoc feature of the
+            Qt5 tool, you can select different strategies for detecting which
+            files should get moced. The simple approach ('0' as the default)
+            scans header and source files for the Q_OBJECT macro, so the
+            trigger 'moc or not' is Q_OBJECT driven. If it is found, the
+            corresponding file gets moced with the <literal>Moc5</literal>
+            builder. This results in the files 'moc_foo.cc' and 'foo.moc' for
+            header and source file, respectively. They get added to the list
+            of sources, for compiling the current library or program. In older
+            Qt manuals, a different technique for mocing is recommended. A cxx
+            file includes the moced output of itself or its header at the end.
+            This approach is somewhat deprecated, but the 'qtsolutions' by Qt
+            are still based on it, for example. You also might have to switch
+            older Qt sources to a new version 5.x.y. Then you can set this
+            variable to '1', for 'include driven' mocing. This means that the
+            tool searches for '#include' statements in all cxx files,
+            containing a file pattern 'moc_foo.cpp' and 'foo.moc' for header
+            and source file, respectively. If the file 'foo.h/foo.cpp' then
+            contains a Q_OBJECT macro, it gets moced but is NOT added to the
+            list of sources. This is the important difference between the two
+            strategies. If no matching include patterns are found for the
+            current cxx file, the Q_OBJECT driven method (#0) is tried as
+            fallback.</para>
+          </listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term>QT5_AUTOMOC_SCANCPPPATH</term>
+
+          <listitem>
+            <para>The default is '1', meaning that the tool scans for mocable
+            files not only in the current directory, but also in all CPPPATH
+            folders (see also <literal>QT5_AUTOMOC_CPPPATH</literal>). You can
+            set this variable to '0' to switch it off on rare occasions, e.g.
+            when too many search folders give you a bad performance in large
+            projects.</para>
+          </listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term>QT5_AUTOMOC_CPPPATH</term>
+
+          <listitem>
+            <para>The list of paths to scan for mocable files (see also
+            <literal>QT5_AUTOMOC_SCANCPPPATH</literal>), it is empty by
+            default which means that the CPPPATH variable is used. You can set
+            this variable to a subset of CPPPATH in order to improve
+            performance, i.e. to minimize the search space.</para>
+          </listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term>QT5_GOBBLECOMMENTS</term>
+
+          <listitem>
+            <para>Default value is '0' (disabled). When you set this variable
+            to '1', you enable the automatic removal of C/C++ comments, while
+            searching for the Q_OBJECT keyword during the Automoc process.
+            This can be helpful if you have the string Q_OBJECT in one of your
+            comments, but don't want this file to get moced.</para>
+          </listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term>QT5_CPPDEFINES_PASSTOMOC</term>
+
+          <listitem>
+            <para>Default value is '1' (enabled). When you set this variable
+            to '1', all currently set CPPDEFINES get passed to the moc
+            executable. It does not matter which strategy you selected with
+            <literal>QT5_AUTOSCAN_STRATEGY</literal> or whether you call the
+            <literal>Moc5</literal> builder directly.</para>
+          </listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term>QT5_CLEAN_TS</term>
+
+          <listitem>
+            <para>Default value is '0' (disabled). When you set this variable
+            to '1', the <literal>Ts5</literal> builder will delete your .ts
+            files on a 'scons -c'. Normally, these files for the QtLinguist
+            are treated as 'precious' (they are not removed prior to a
+            rebuild) and do not get cleaned.</para>
+          </listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term>QT5_XMOCHPREFIX</term>
+
+          <listitem>
+            <para>Default value is 'moc_'. Like
+            <literal>QT5_MOCHPREFIX</literal>, this is the prefix for moc
+            output files, when the source file is a header and the Automoc
+            strategy #1 (include driven) is used.</para>
+          </listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term>QT5_XMOCHSUFFIX</term>
+
+          <listitem>
+            <para>Default value is '.cpp'. Like
+            <literal>QT5_MOCHSUFFIX</literal>, this is the suffix for moc
+            output files, when the source file is a header and the Automoc
+            strategy #1 (include driven) is used.</para>
+          </listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term>QT5_XMOCCXXPREFIX</term>
+
+          <listitem>
+            <para>Default value is ''. Like
+            <literal>QT5_MOCCXXPREFIX</literal>, this is the prefix for moc
+            output files, when source is a cxx file and the Automoc strategy
+            #1 (include driven) is used.</para>
+          </listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term>QT5_XMOCCXXSUFFIX</term>
+
+          <listitem>
+            <para>Default value is '.moc'. Like
+            <literal>QT5_MOCCXXSUFFIX</literal>, this is the suffix for moc
+            output files, when source is a cxx file and the Automoc strategy
+            #1 (include driven) is used.</para>
+          </listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term>QT5_RCC</term>
+
+          <listitem>
+            <para>Default value is '<literal>QT5_BINPATH</literal>/rcc'. The
+            path to the Qt5 rcc executable (resource file compiler).</para>
+          </listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term>QT5_LUPDATE</term>
+
+          <listitem>
+            <para>Default value is '<literal>QT5_BINPATH</literal>/lupdate'.
+            The path to the Qt5 lupdate executable (updates the .ts files from
+            sources).</para>
+          </listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term>QT5_LRELEASE</term>
+
+          <listitem>
+            <para>Default value is '<literal>QT5_BINPATH</literal>/lrelease'.
+            The path to the Qt5 lrelease executable (converts .ts files to
+            binary .qm files).</para>
+          </listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term>QT5_QRCFLAGS</term>
+
+          <listitem>
+            <para>Default value is ''. These flags are passed to the Qt5 rcc
+            executable, when compiling a resource file.</para>
+          </listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term>QT5_LUPDATEFLAGS</term>
+
+          <listitem>
+            <para>Default value is ''. These flags are passed to the Qt5
+            lupdate executable, when updating .ts files from sources.</para>
+          </listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term>QT5_LRELEASEFLAGS</term>
+
+          <listitem>
+            <para>Default value is ''. These flags are passed to the Qt5
+            lrelease executable, when compiling .ts files into binary .qm
+            files.</para>
+          </listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term>QT5_MOCINCPREFIX</term>
+
+          <listitem>
+            <para>Default value is '-I'. The prefix for specifying include
+            directories to the Qt5 moc executable.</para>
+          </listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term>QT5_QRCSUFFIX</term>
+
+          <listitem>
+            <para>Default value is '.qrc'. Suffix of Qt5 resource
+            files.</para>
+          </listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term>QT5_QRCCXXSUFFIX</term>
+
+          <listitem>
+            <para>Default value is '$CXXFILESUFFIX'. This is the suffix for
+            compiled .qrc resource files.</para>
+          </listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term>QT5_QRCCXXPREFIX</term>
+
+          <listitem>
+            <para>Default value is 'qrc_'. This is the prefix for compiled
+            .qrc resource files.</para>
+          </listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term>QT5_MOCINCFLAGS</term>
+
+          <listitem>
+            <para>List of include paths for the Qt5 moc executable, is
+            compiled from <literal>QT5_MOCINCPREFIX</literal>,
+            <literal>QT5_MOCCPPPATH</literal> and
+            <literal>INCSUFFIX</literal>.</para>
+          </listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term>QT5_MOCDEFINES</term>
+
+          <listitem>
+            <para>List of CPP defines that are passed to the Qt5 moc
+            executable, is compiled from <literal>QT5_MOCDEFPREFIX</literal>,
+            <literal>CPPDEFINES</literal> and
+            <literal>QT5_MOCDEFSUFFIX</literal>.</para>
+          </listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term>QT5_LUPDATECOM</term>
+
+          <listitem>
+            <para>Command to update .ts files for translation from the
+            sources.</para>
+          </listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term>QT5_LUPDATECOMSTR</term>
+
+          <listitem>
+            <para>The string displayed when updating .ts files from the
+            sources. If this is not set, then
+            <literal>QT5_LUPDATECOM</literal> (the command line) is
+            displayed.</para>
+          </listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term>QT5_LRELEASECOM</term>
+
+          <listitem>
+            <para>Command to convert .ts files to binary .qm files.</para>
+          </listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term>QT5_LRELEASECOMSTR</term>
+
+          <listitem>
+            <para>The string displayed when converting .ts files to binary .qm
+            files. If this is not set, then <literal>QT5_RCC</literal> (the
+            command line) is displayed.</para>
+          </listitem>
+        </varlistentry>
+      </variablelist></para>
+  </section>
+</article>
\ No newline at end of file
diff --git a/workspace/sconstools3/qt6/docs/scons.css b/workspace/sconstools3/qt6/docs/scons.css
new file mode 100644
index 0000000..6941abb
--- /dev/null
+++ b/workspace/sconstools3/qt6/docs/scons.css
@@ -0,0 +1,263 @@
+body {	
+	background: #ffffff;
+	margin: 10px; 
+	padding: 0;
+	font-family:palatino, georgia, verdana, arial, sans-serif;
+	}
+
+
+a {
+	color: #80572a;
+	}
+
+a:hover {
+	color: #d72816;
+	text-decoration: none;
+	}
+
+tt {
+	color: #a14447;
+	}
+
+pre {
+     background: #e0e0e0;
+    }
+
+#main {
+	border: 1px solid;
+	border-color: black;
+	background-color: white;
+	background-image: url(../images/sconsback.png);
+	background-repeat: repeat-y 50% 0;
+	background-position: right top;
+	margin: 30px auto;
+	width: 750px;
+	}
+	
+#banner {
+	background-image: url(../images/scons-banner.jpg);
+	border-bottom: 1px solid;
+	height: 95px;
+	}
+
+#menu {
+	font-family: sans-serif;
+	font-size: small;
+	line-height: 0.9em;
+	float: right;
+	width: 220px;
+	clear: both;
+	margin-top: 10px;
+	}
+
+#menu li {
+	margin-bottom: 7px;
+	}
+
+#menu li li {
+	margin-bottom: 2px;
+	}
+
+#menu li.submenuitems {
+	margin-bottom: 2px;
+	}
+
+#menu a {
+	text-decoration: none;
+	}
+
+#footer {
+	border-top: 1px solid black;
+	text-align: center;
+	font-size: small;
+	color: #822;
+	margin-top: 4px;
+	background: #eee;
+	}
+
+ul.hack {
+	list-style-position:inside;
+	}
+
+ul.menuitems {
+	list-style-type: none;
+	}
+
+ul.submenuitems {
+	list-style-type: none;
+	font-size: smaller;
+	margin-left: 0;
+	padding-left: 16px;
+	}
+
+ul.subsubmenuitems {
+	list-style-type: none;
+	font-size: smaller;
+	margin-left: 0;
+	padding-left: 16px;
+	}
+
+ol.upper-roman {
+	list-style-type: upper-roman;
+	}
+
+ol.decimal {
+	list-style-type: decimal;
+	}
+
+#currentpage {
+	font-weight: bold;
+	}
+
+#bodycontent {
+	margin: 15px;
+	width: 520px;
+	font-size: small;
+	line-height: 1.5em;
+	}
+
+#bodycontent li {
+	margin-bottom: 6px;
+	list-style-type: square;
+	}
+
+#sconsdownloadtable downloadtable {
+        display: table;
+        margin-left: 5%;
+        border-spacing: 12px 3px;
+        }
+
+#sconsdownloadtable downloadrow {
+        display: table-row;
+        }
+
+#sconsdownloadtable downloadentry {
+        display: table-cell;
+	text-align: center;
+        vertical-align: bottom;
+        }
+
+#sconsdownloadtable downloaddescription {
+        display: table-cell;
+        font-weight: bold;
+	text-align: left;
+        }
+
+#sconsdownloadtable downloadversion {
+        display: table-cell;
+        font-weight: bold;
+	text-align: center;
+        }
+
+#sconsdocversiontable sconsversiontable {
+        display: table;
+        margin-left: 10%;
+        border-spacing: 12px 3px;
+        }
+
+#sconsdocversiontable sconsversionrow {
+        display: table-row;
+        }
+
+#sconsdocversiontable docformat {
+        display: table-cell;
+        font-weight: bold;
+	text-align: center;
+        vertical-align: bottom;
+        }
+
+#sconsdocversiontable sconsversion {
+        display: table-cell;
+        font-weight: bold;
+	text-align: left;
+        }
+
+#sconsdocversiontable docversion {
+        display: table-cell;
+        font-weight: bold;
+	text-align: center;
+        }
+
+#osrating {
+	margin-left: 35px;
+	}
+	
+
+h2 {
+	color: #272;
+	color: #c01714;
+	font-family: sans-serif;
+	font-weight: normal;
+	}
+
+h2.pagetitle {
+	font-size: xx-large;
+	}
+h3 {
+	margin-bottom: 10px;
+	}
+
+.date {
+	font-size: small;
+	color: gray;
+	}
+	
+.link {
+	margin-bottom: 22px;
+	}
+
+.linkname {
+	}
+
+.linkdesc {
+	margin: 10px;
+	margin-top: 0;
+	}
+
+.quote {
+	margin-top: 20px;
+	margin-bottom: 10px;
+	background: #f8f8f8;
+	border: 1px solid;
+	border-color: #ddd;
+	}
+
+.quotetitle {
+	font-weight: bold;
+	font-size: large;
+	margin: 10px;
+	}
+
+.quotedesc {
+	margin-left: 20px;
+	margin-right: 10px;
+	margin-bottom: 15px;
+	}
+
+.quotetext {
+	margin-top: 20px;
+	margin-left: 20px;
+	margin-right: 10px;
+	font-style: italic;
+	}
+
+.quoteauthor {
+	font-size: small;
+	text-align: right;
+	margin-top: 10px;
+	margin-right: 7px;
+	}
+
+.sconslogo {
+	font-style: normal;
+	font-weight: bold;
+	color: #822;
+	}
+	
+.downloadlink {
+	}
+
+.downloaddescription {
+	margin-left: 1em;
+	margin-bottom: 0.4em;
+	}


Test patch:
diff --git a/nexxT/tests/__init__.py b/nexxT/tests/__init__.py
index e41edca..147a6b5 100644
--- a/nexxT/tests/__init__.py
+++ b/nexxT/tests/__init__.py
@@ -6,6 +6,7 @@
 
 from pathlib import Path
 from nexxT.interface import FilterSurrogate
+import nexxT.Qt.QtMultimedia
 
 CSimpleSource = FilterSurrogate(
     "binary://" + str((Path(__file__).parent /
diff --git a/nexxT/tests/core/test_ActiveApplication.py b/nexxT/tests/core/test_ActiveApplication.py
index 613b394..340eef7 100644
--- a/nexxT/tests/core/test_ActiveApplication.py
+++ b/nexxT/tests/core/test_ActiveApplication.py
@@ -11,7 +11,8 @@
 import os
 import time
 import pprint
-from PySide2.QtCore import QCoreApplication, QTimer
+import nexxT.Qt
+from nexxT.Qt.QtCore import QCoreApplication, QTimer
 
 def setup():
     global app
@@ -111,7 +112,7 @@ def state_changed(state):
         aa.open()
         aa.start()
 
-        app.exec_()
+        nexxT.Qt.call_exec(app)
 
         return events
     finally:
diff --git a/nexxT/tests/core/test_CompositeFilter.py b/nexxT/tests/core/test_CompositeFilter.py
index 6301eee..3f2eb80 100644
--- a/nexxT/tests/core/test_CompositeFilter.py
+++ b/nexxT/tests/core/test_CompositeFilter.py
@@ -6,7 +6,8 @@
 
 import os
 import time
-from PySide2.QtCore import QCoreApplication, QTimer
+import nexxT.Qt
+from nexxT.Qt.QtCore import QCoreApplication, QTimer
 from nexxT.interface import FilterState
 from nexxT.core.CompositeFilter import CompositeFilter
 from nexxT.core.Application import Application
@@ -141,7 +142,7 @@ def state_changed(state):
         aa.open()
         aa.start()
 
-        app.exec_()
+        nexxT.Qt.call_exec(app)
 
         return events
     finally:
@@ -302,7 +303,7 @@ def state_changed(state):
         aa.open()
         aa.start()
 
-        app.exec_()
+        nexxT.Qt.call_exec(app)
 
         return events
     finally:
diff --git a/nexxT/tests/core/test_ConfigFiles.py b/nexxT/tests/core/test_ConfigFiles.py
index ada1e0a..6b8f142 100644
--- a/nexxT/tests/core/test_ConfigFiles.py
+++ b/nexxT/tests/core/test_ConfigFiles.py
@@ -8,7 +8,8 @@
 import logging
 from pathlib import Path
 import pytest
-from PySide2.QtCore import QCoreApplication, QTimer
+import nexxT.Qt
+from nexxT.Qt.QtCore import QCoreApplication, QTimer
 from nexxT.interface import FilterState, Services
 from nexxT.core.ConfigFiles import ConfigFileLoader
 from nexxT.core.Application import Application
@@ -85,7 +86,7 @@ def state_changed(state):
         aa.open()
         aa.start()
 
-        app.exec_()
+        nexxT.Qt.call_exec(app)
     finally:
         del t
         del t2
diff --git a/nexxT/tests/core/test_EntryPoints.py b/nexxT/tests/core/test_EntryPoints.py
index 41432d3..5345c2c 100644
--- a/nexxT/tests/core/test_EntryPoints.py
+++ b/nexxT/tests/core/test_EntryPoints.py
@@ -16,10 +16,15 @@
                 "tests.nexxT.CSimpleSource",
                 "tests.nexxT.CTestExceptionFilter"])
 
+blacklist = set(["examples.videoplayback.AviReader",
+                 "examples.framework.CameraGrabber"])
+
 @pytest.mark.parametrize("ep",
-                         [pytest.param(e.name, marks=pytest.mark.skipif(not nexxT.useCImpl and e.name in cfilters,
-                                                                        reason="testing a pure python variant"))
-                            for e in pkg_resources.iter_entry_points("nexxT.filters")])
+    [pytest.param(e.name,
+     marks=[pytest.mark.skipif(not nexxT.useCImpl and e.name in cfilters, reason="testing a pure python variant"),
+            pytest.mark.skipif(e.name in blacklist, reason="testing blacklisted filter")
+           ]
+    ) for e in pkg_resources.iter_entry_points("nexxT.filters")])
 def test_EntryPoint(ep):
     env = FilterEnvironment("entry_point://" + ep, "entry_point", PropertyCollectionImpl('propColl', None))
     PluginManager.singleton().unloadAll()
diff --git a/nexxT/tests/core/test_FilterExceptions.py b/nexxT/tests/core/test_FilterExceptions.py
index a888291..5594fbb 100644
--- a/nexxT/tests/core/test_FilterExceptions.py
+++ b/nexxT/tests/core/test_FilterExceptions.py
@@ -9,7 +9,8 @@
 from pathlib import Path
 import pytest
 import pytestqt
-from PySide2.QtCore import QCoreApplication, QTimer
+import nexxT.Qt
+from nexxT.Qt.QtCore import QCoreApplication, QTimer
 from nexxT.interface import FilterState, Services
 from nexxT.core.ConfigFiles import ConfigFileLoader
 from nexxT.core.Application import Application
@@ -99,7 +100,7 @@ def state_changed(state):
             aa.open()
             aa.start()
 
-            app.exec_()
+            nexxT.Qt.call_exec(app)
         finally:
             del t
             del t2
diff --git a/nexxT/tests/core/test_Graph.py b/nexxT/tests/core/test_Graph.py
index 8079b72..27c06b9 100644
--- a/nexxT/tests/core/test_Graph.py
+++ b/nexxT/tests/core/test_Graph.py
@@ -4,7 +4,7 @@
 # THE PROGRAM IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND.
 #
 
-from PySide2.QtCore import QCoreApplication
+from nexxT.Qt.QtCore import QCoreApplication
 from nexxT.interface import Services
 from nexxT.core.Graph import FilterGraph
 from nexxT.core.PropertyCollectionImpl import PropertyCollectionImpl
diff --git a/nexxT/tests/core/test_PropertyCollectionImpl.py b/nexxT/tests/core/test_PropertyCollectionImpl.py
index 334aa8d..1898d4b 100644
--- a/nexxT/tests/core/test_PropertyCollectionImpl.py
+++ b/nexxT/tests/core/test_PropertyCollectionImpl.py
@@ -8,7 +8,7 @@
 from nexxT.core.PropertyHandlers import defaultHandler
 from nexxT.interface import PropertyHandler
 from nexxT.core.Exceptions import *
-from PySide2.QtCore import QRegExp, QCoreApplication
+from nexxT.Qt.QtCore import QCoreApplication
 
 class MySimplePropertyHandler(PropertyHandler):
     def __init__(self, options):
diff --git a/nexxT/tests/integration/basicworkflow_script.py b/nexxT/tests/integration/basicworkflow_script.py
index 558c77e..c5094cd 100644
--- a/nexxT/tests/integration/basicworkflow_script.py
+++ b/nexxT/tests/integration/basicworkflow_script.py
@@ -6,7 +6,7 @@
 
 import glob
 import logging
-from PySide2.QtCore import Qt, QCoreApplication, QTimer, QModelIndex
+from nexxT.Qt.QtCore import Qt, QCoreApplication, QTimer, QModelIndex
 from nexxT.interface import Services, FilterState
 from nexxT.core.Utils import MethodInvoker, waitForSignal
 from nexxT.core.Application import Application
diff --git a/nexxT/tests/integration/test_Latency.py b/nexxT/tests/integration/test_Latency.py
index f348cd8..73f6a4f 100644
--- a/nexxT/tests/integration/test_Latency.py
+++ b/nexxT/tests/integration/test_Latency.py
@@ -35,7 +35,7 @@ def test_latency():
     try:
         p = subprocess.run(
             [sys.executable, "-m", "nexxT.core.AppConsole", "--gui", "false", "-a", "test_latency", "-e", """\
-from PySide2.QtCore import QTimer, QObject, QCoreApplication
+from nexxT.Qt.QtCore import QTimer, QObject, QCoreApplication
 from nexxT.core.Application import Application
 from nexxT.interface import Services, FilterState 
 conf = Services.getService("Configuration")
diff --git a/nexxT/tests/integration/test_gui.py b/nexxT/tests/integration/test_gui.py
index c675396..bb831d5 100644
--- a/nexxT/tests/integration/test_gui.py
+++ b/nexxT/tests/integration/test_gui.py
@@ -12,15 +12,17 @@
 You can also pass --no-xvfb to pytest and also --keep-open for inspecting the issue in head mode.
 """
 
+import json
 import os
 import logging
 from pathlib import Path
 import re
 import pytest
-import shiboken2
-from PySide2.QtCore import QItemSelection, Qt, QTimer, QSize, QPoint, QModelIndex
-from PySide2.QtWidgets import QGraphicsSceneContextMenuEvent, QWidget, QApplication, QTreeView
+import nexxT.shiboken
+from nexxT.Qt.QtCore import QItemSelection, Qt, QTimer, QSize, QPoint, QModelIndex
+from nexxT.Qt.QtWidgets import QGraphicsSceneContextMenuEvent, QWidget, QApplication, QTreeView
 from nexxT.core.AppConsole import startNexT
+from nexxT.core import Compatibility
 from nexxT.interface import Services
 from nexxT.services.gui.GraphEditorView import GraphEditorView
 
@@ -99,8 +101,9 @@ def activeMenuEntry():
             act = menu.activeAction()
             if act is None:
                 return None
-            while act.menu() is not None and act.menu().activeAction() is not None:
-                act = act.menu().activeAction()
+
+            while Compatibility.getMenuFromAction(act) is not None and Compatibility.getMenuFromAction(act).activeAction() is not None:
+                act = Compatibility.getMenuFromAction(act).activeAction()
             return act.text()
 
         if kwargs.get("debug", False):
@@ -607,7 +610,7 @@ def _first(self):
             self.addConnectionToGraphEditor(gevc, gevc_out.inPortItems[1], n2.outPortItems[0])
             # add composite filter to gev
             comp = self.addNodeToGraphEditor(gev, QPoint(20,20), CM_FILTER_FROM_COMPOSITE, "composite")
-            shiboken2.delete(gevc.parent())
+            nexxT.shiboken.delete(gevc.parent())
             self.qtbot.wait(self.delay)
             # auto layout
             QTimer.singleShot(self.delay, lambda: self.activateContextMenu(CM_AUTOLAYOUT))
@@ -669,7 +672,7 @@ def _first(self):
             # start and delete a graph editor for the old application
             gev = self.startGraphEditor(conf, mw, "application")
             self.qtbot.wait(self.delay)
-            shiboken2.delete(gev.parent())
+            nexxT.shiboken.delete(gev.parent())
             self.qtbot.wait(self.delay)
             # start the editor for the new application
             gev = self.startGraphEditor(conf, mw, "application_2")
@@ -1271,10 +1274,13 @@ class DeadlockTestIssue25(GuiTestBase):
     """
     Concrete test class for the test_property test case
     """
-    def __init__(self, qtbot, xvfb, keep_open, delay, tmpdir):
+    def __init__(self, qtbot, xvfb, keep_open, delay, tmpdir, change_conn):
         super().__init__(qtbot, xvfb, keep_open, delay, tmpdir)
+        self.change_conn = change_conn
+        self.tmpdir = tmpdir
 
     def _stage0(self):
+        tmpdir = self.tmpdir
         conf = None
         mw = None
         try:
@@ -1283,7 +1289,17 @@ def _stage0(self):
             conf = Services.getService("Configuration")
             log = Services.getService("Logging")
             # load recent config
-            fn = str((Path(__file__).parent.parent / "core" / "test_deadlock.json").absolute())
+            if self.change_conn is None:
+                fn = str((Path(__file__).parent.parent / "core" / "test_deadlock.json").absolute())
+            else:
+                cfg = json.load((Path(__file__).parent.parent / "core" / "test_deadlock.json").open("rb"))
+                conns = cfg["applications"][0]["connections"]
+                conns = conns[:self.change_conn] + conns[self.change_conn+1:]
+                cfg["applications"][0]["connections"] = conns
+                fn = Path(tmpdir) / "test_deadlock.json"
+                with fn.open("w") as fp:
+                    json.dump(cfg, fp)
+                fn = str(fn.absolute())
             logger.info("laoding fn=%s", fn)
             QTimer.singleShot(self.delay, lambda: self.enterText(fn))
             conf.actLoad.trigger()
@@ -1291,10 +1307,15 @@ def _stage0(self):
 
             appidx = conf.model.indexOfSubConfig(conf.configuration().applicationByName("deadlock"))
             self.cmContextMenu(conf, appidx, CM_INIT_APP)
-            self.qtbot.wait(1000)
-            logMsg = "This graph is not deadlock-safe. A cycle has been found in the thread graph: main->compute->main"
-            self.noWarningsInLog(log, ignore=[logMsg])
-            self.assertLogItem(log, "ERROR", logMsg)
+            if self.change_conn in [None, 0, 2]:
+                self.qtbot.wait(1000)
+                logMsg = "This graph is not deadlock-safe. A cycle has been found in the thread graph: main->compute->main"
+                self.noWarningsInLog(log, ignore=[logMsg])
+                self.assertLogItem(log, "ERROR", logMsg)
+            else:
+                self.qtbot.wait(10000)
+                self.noWarningsInLog(log)
+
             # assert that the samples arrived in the correct order
             def assertSampleOrder():
                 numRows = log.logWidget.model().rowCount(QModelIndex())
@@ -1328,8 +1349,9 @@ def test(self):
 @pytest.mark.gui
 @pytest.mark.parametrize("delay", [300])
 @pytest.mark.timeout(60, method="thread")
-def test_deadlock_issue25(qtbot, xvfb, keep_open, delay, tmpdir):
-    test = DeadlockTestIssue25(qtbot, xvfb, keep_open, delay, tmpdir)
+@pytest.mark.parametrize("change_conn", [None, 0, 1, 2, 3])
+def test_deadlock_issue25(qtbot, xvfb, keep_open, delay, tmpdir, change_conn):
+    test = DeadlockTestIssue25(qtbot, xvfb, keep_open, delay, tmpdir, change_conn)
     test.test()
 
 class ExecutionOrderTest(GuiTestBase):
@@ -1508,7 +1530,7 @@ def generateFilter(self, messageAtOpen):
         myfilter = self.tmpdir / "myfilter.py"
         myfilter.write_text("""
 import logging
-from PySide2.QtWidgets import QLabel
+from nexxT.Qt.QtWidgets import QLabel
 from nexxT.interface import Filter, InputPort, Services
 
 logger = logging.getLogger(__name__)
@@ -1608,6 +1630,9 @@ def _stage0(self):
             assert mdigeom == self.getMdiWindow().geometry()
             self.assertLogItem(log, "INFO", "myfilter version 3")
             # save again to create gui state
+            # can't save again without setting config to dirty
+            conf._dirtyChanged(True)
+            assert conf.actSave.isEnabled()
             conf.actSave.trigger()
             assert self.guistatefile.exists()
             # move window
diff --git a/nexxT/tests/interface/AviFilePlayback.py b/nexxT/tests/interface/AviFilePlayback.py
index 7ca40b0..0c9ef89 100644
--- a/nexxT/tests/interface/AviFilePlayback.py
+++ b/nexxT/tests/interface/AviFilePlayback.py
@@ -6,9 +6,9 @@
 
 import logging
 import time
-from PySide2.QtCore import Signal, Slot, QTimer, QUrl
-from PySide2.QtMultimedia import QMediaPlayer, QMediaPlaylist, QAbstractVideoSurface, QVideoFrame
-from PySide2.QtMultimediaWidgets import QVideoWidget
+from nexxT.Qt.QtCore import Signal, Slot, QTimer, QUrl
+from nexxT.Qt.QtMultimedia import QMediaPlayer, QMediaPlaylist, QAbstractVideoSurface, QVideoFrame
+from nexxT.Qt.QtMultimediaWidgets import QVideoWidget
 from nexxT.interface import Filter, OutputPort, DataSample, Services
 
 logger = logging.getLogger(__name__)
diff --git a/nexxT/tests/interface/SimpleStaticFilter.py b/nexxT/tests/interface/SimpleStaticFilter.py
index ce2b2d2..8b6ad5b 100644
--- a/nexxT/tests/interface/SimpleStaticFilter.py
+++ b/nexxT/tests/interface/SimpleStaticFilter.py
@@ -6,9 +6,9 @@
 
 import logging
 import time
-from PySide2.QtWidgets import QLabel
+from nexxT.Qt.QtWidgets import QLabel
 from nexxT.interface import Filter, InputPort, OutputPort, DataSample, Services
-from PySide2.QtCore import QTimer
+from nexxT.Qt.QtCore import QTimer
 
 class SimpleStaticFilter(Filter):
 
diff --git a/nexxT/tests/src/AviFilePlayback.cpp b/nexxT/tests/src/AviFilePlayback.cpp
index 0b98d7c..05795dc 100644
--- a/nexxT/tests/src/AviFilePlayback.cpp
+++ b/nexxT/tests/src/AviFilePlayback.cpp
@@ -6,9 +6,9 @@
  */
 
 #include "AviFilePlayback.hpp"
-#include "Services.hpp"
-#include "PropertyCollection.hpp"
-#include "Logger.hpp"
+#include "nexxT/Services.hpp"
+#include "nexxT/PropertyCollection.hpp"
+#include "nexxT/Logger.hpp"
 #include "ImageFormat.h"
 #include <QtCore/QThread>
 #include <QtCore/QBuffer>
@@ -25,25 +25,25 @@ void VideoPlaybackDevice::openVideo()
     }
     NEXXT_LOG_DEBUG("entering openVideo");
     pauseOnStream = QString();
-    player = new QMediaPlayer(this, QMediaPlayer::VideoSurface);
-    player->setMuted(true);
+    player = new QMediaPlayer(this);
     videoSurface = new VideoGrabber(this);
     connect(player, SIGNAL(durationChanged(qint64)),
             this, SLOT(newDuration(qint64)));
     connect(player, SIGNAL(positionChanged(qint64)),
             this, SLOT(newPosition(qint64)));
-    connect(player, SIGNAL(currentMediaChanged(const QMediaContent &)),
-            this, SLOT(currentMediaChanged(const QMediaContent &)));
+    connect(player, SIGNAL(sourceChanged(const QUrl &)),
+            this, SLOT(currentMediaChanged(const QUrl &)));
     connect(videoSurface, SIGNAL(newImage(const QImage &)),
             this, SLOT(newImage(const QImage &)));
-    connect(player, SIGNAL(error(QMediaPlayer::Error)),
-            this, SLOT(mediaPlayerError(QMediaPlayer::Error)));
-    connect(player, SIGNAL(stateChanged(QMediaPlayer::State)),
-            this, SLOT(mediaPlayerStateChanged(QMediaPlayer::State)));
+    connect(player, SIGNAL(errorOccurred(QMediaPlayer::Error, const QString &)),
+            this, SLOT(mediaPlayerError(QMediaPlayer::Error, const QString &)));
+    connect(player, SIGNAL(playbackStateChanged(QMediaPlayer::PlaybackState)),
+            this, SLOT(mediaPlayerStateChanged(QMediaPlayer::PlaybackState)));
     connect(player, SIGNAL(playbackRateChanged(qreal)),
             this, SLOT(mediaPlayerPlaybackRateChanged(qreal)));
-    player->setMedia(QUrl::fromLocalFile(filename));
+    player->setSource(QUrl::fromLocalFile(filename));
     player->setVideoOutput(videoSurface);
+    player->setAudioOutput(0);
     player->setPlaybackRate(playbackRate);
     player->pause();
     NEXXT_LOG_DEBUG("leaving openVideo");
@@ -51,20 +51,21 @@ void VideoPlaybackDevice::openVideo()
 
 void VideoPlaybackDevice::closeVideo()
 {
-    NEXXT_LOG_DEBUG("entering closeVideo");
-    if(player)
-    {
-        delete player;
-        player = nullptr;
-    }
+    NEXXT_LOG_INFO("entering closeVideo");
+    NEXXT_LOG_INFO("emitting playback paused.");
+    emit playbackPaused();
     if(videoSurface)
     {
-        delete videoSurface;
+        videoSurface->deleteLater();
         videoSurface = nullptr;
     }
-    NEXXT_LOG_INFO("emitting playback paused.");
-    emit playbackPaused();
-    NEXXT_LOG_DEBUG("leaving closeVideo");
+    if(player)
+    {
+        player->stop();
+        player->deleteLater();
+        player = nullptr;
+    }
+    NEXXT_LOG_INFO("leaving closeVideo");
 }
 
 VideoPlaybackDevice::VideoPlaybackDevice(BaseFilterEnvironment *env) :
@@ -127,20 +128,24 @@ void VideoPlaybackDevice::newImage(const QImage &_img)
     );
 }
 
-void VideoPlaybackDevice::mediaPlayerError(QMediaPlayer::Error)
+void VideoPlaybackDevice::mediaPlayerError(QMediaPlayer::Error, const QString &msg)
 {
-    if(player) NEXXT_LOG_WARN(QString("error from QMediaPlayer: %1").arg(player->errorString()));
+    if(player) NEXXT_LOG_WARN(QString("error from QMediaPlayer: %1").arg(msg));
 }
 
-void VideoPlaybackDevice::mediaPlayerStateChanged(QMediaPlayer::State newState)
+void VideoPlaybackDevice::mediaPlayerStateChanged(QMediaPlayer::PlaybackState newState)
 {
     if(newState == QMediaPlayer::PlayingState)
     {
+        NEXXT_LOG_INFO("emitting playback started.");
         emit playbackStarted();
-    } else
+    } else if(newState == QMediaPlayer::PausedState)
     {
         NEXXT_LOG_INFO("emitting playback paused.");
         emit playbackPaused();
+    } else
+    {
+        NEXXT_LOG_INFO("unknown state.");
     }
 }
 
@@ -164,7 +169,7 @@ void VideoPlaybackDevice::newPosition(qint64 position)
     emit currentTimestampChanged(position*1000000);
 }
 
-void VideoPlaybackDevice::currentMediaChanged(const QMediaContent &)
+void VideoPlaybackDevice::currentMediaChanged(const QUrl &)
 {
     NEXXT_LOG_DEBUG("currentMediaChanged called");
 }
@@ -185,7 +190,7 @@ void VideoPlaybackDevice::stepForward(const QString &stream)
 {
     NEXXT_LOG_DEBUG(QString("stepForward(%1) called").arg(stream));
     pauseOnStream = "video";
-    if( player && player->state() != QMediaPlayer::PlayingState )
+    if( player && player->playbackState() != QMediaPlayer::PlayingState )
     {
         NEXXT_LOG_DEBUG("calling play");
         if(player) player->play();
diff --git a/nexxT/tests/src/AviFilePlayback.hpp b/nexxT/tests/src/AviFilePlayback.hpp
index e53a9b1..59feb6e 100644
--- a/nexxT/tests/src/AviFilePlayback.hpp
+++ b/nexxT/tests/src/AviFilePlayback.hpp
@@ -11,9 +11,9 @@
 #include <QtCore/QObject>
 #include <QtMultimedia/QMediaPlayer>
 #include "VideoGrabber.hpp"
-#include "Filters.hpp"
-#include "Ports.hpp"
-#include "NexxTPlugins.hpp"
+#include "nexxT/Filters.hpp"
+#include "nexxT/Ports.hpp"
+#include "nexxT/NexxTPlugins.hpp"
 
 using namespace nexxT;
 
@@ -49,13 +49,13 @@ class VideoPlaybackDevice : public Filter
 
 public slots:
     void newImage(const QImage &img);
-    void mediaPlayerError(QMediaPlayer::Error);
-    void mediaPlayerStateChanged(QMediaPlayer::State newState);
+    void mediaPlayerError(QMediaPlayer::Error, const QString &);
+    void mediaPlayerStateChanged(QMediaPlayer::PlaybackState newState);
     void mediaPlayerPlaybackRateChanged(qreal newRate);
 
     void newDuration(qint64 duration);
     void newPosition(qint64 position);
-    void currentMediaChanged(const QMediaContent &);
+    void currentMediaChanged(const QUrl &);
     void startPlayback();
     void pausePlayback();
     void stepForward(const QString &stream);
diff --git a/nexxT/tests/src/CameraGrabber.cpp b/nexxT/tests/src/CameraGrabber.cpp
index aef22a0..b5234a9 100644
--- a/nexxT/tests/src/CameraGrabber.cpp
+++ b/nexxT/tests/src/CameraGrabber.cpp
@@ -6,7 +6,10 @@
  */
 
 #include "CameraGrabber.hpp"
-#include "Logger.hpp"
+#include "nexxT/Logger.hpp"
+#include "nexxT/PropertyCollection.hpp"
+#include <QtMultimedia/QCameraDevice>
+#include <QtMultimedia/QMediaDevices>
 #include <cstring>
 
 using namespace nexxT;
@@ -16,6 +19,7 @@ CameraGrabber::CameraGrabber(BaseFilterEnvironment *env)
     : Filter(false, false, env)
     , camera()
     , videoSurface()
+    , session()
 {
     /* similar to the python API, we create an output port for transmitting images */
     video_out = SharedOutputPortPtr(new OutputPortInterface(false, "video_out", env));
@@ -24,6 +28,13 @@ CameraGrabber::CameraGrabber(BaseFilterEnvironment *env)
     /* note that we do not connect to the hardware in the constructor, this is to be
      * done later in onOpen(...) for efficiency reasons.
      */
+    PropertyCollection *pc = propertyCollection();
+    QStringList devices;
+    for (const QCameraDevice &cameraDevice : QMediaDevices::videoInputs())
+    {
+        devices.push_back(cameraDevice.description());
+    }
+    pc->defineProperty("device", devices[0], "the camera device", {{"enum", devices}});
 }
 
 CameraGrabber::~CameraGrabber()
@@ -77,13 +88,13 @@ void CameraGrabber::newImage(const QImage &_img)
 // LITERAL_INCLUDE_END_2
 
 /* in case of an error, we restart the camera stream */
-void CameraGrabber::onStateChanged(QCamera::State state)
+void CameraGrabber::onErrorOccurred(QCamera::Error error, const QString &errString)
 {
     static bool recursive = false;
-    if( camera->error() != QCamera::NoError && !recursive )
+    if( error != QCamera::NoError && !recursive )
     {
         recursive = true;
-        NEXXT_LOG_ERROR(QString("Error from QCamera: %1").arg(camera->errorString()));
+        NEXXT_LOG_ERROR(QString("Error from QCamera: %1").arg(errString));
         NEXXT_LOG_INFO("Trying to recover");
         camera->stop();
         camera->start();
@@ -95,31 +106,55 @@ void CameraGrabber::onStateChanged(QCamera::State state)
 /* here we connect to the hardware */
 void CameraGrabber::onOpen()
 {
+    if(videoSurface)
+    {
+        NEXXT_LOG_WARN("videoSurface still allocated in onOpen");
+        delete videoSurface;
+        videoSurface = 0;
+    }
     if(camera)
     {
         NEXXT_LOG_WARN("camera still allocated in onOpen");
         delete camera;
+        camera = 0;
     }
-    if(videoSurface)
+    if(session)
     {
-        NEXXT_LOG_WARN("videoSurface still allocated in onOpen");
-        delete videoSurface;
+        NEXXT_LOG_WARN("session still allocated in onOpen");
+        delete session;
+        session = 0;
     }
     /* create a QCamera and a VideoGrabber instance, the camera will run in a default mode
      * All these objects run in the same thread as this filter.
      */
-    camera = new QCamera(this);
+    PropertyCollection *pc = propertyCollection();
+    QString devname = pc->getProperty("device").value<QString>();
+    for (const QCameraDevice &cameraDevice : QMediaDevices::videoInputs())
+    {
+        if( cameraDevice.description() == devname )
+        {
+            camera = new QCamera(cameraDevice, this);
+            break;
+        }
+    }
+    session = new QMediaCaptureSession(this);
+    if(!camera)
+    {
+        NEXXT_LOG_WARN("Using default camera.");
+        camera = new QCamera(this);
+    }
     videoSurface = new VideoGrabber(this);
+    session->setCamera(camera);
+    session->setVideoOutput(videoSurface);
     /* make up signal/slot connections */
     QObject::connect(videoSurface, SIGNAL(newImage(const QImage &)), this, SLOT(newImage(const QImage &)));
-    QObject::connect(camera, SIGNAL(stateChanged(QCamera::State)), this, SLOT(onStateChanged(QCamera::State)));
+    QObject::connect(camera, SIGNAL(errorOccurred(QCamera::Error, const QString &)),
+                     this, SLOT(onErrorOccurred(QCamera::Error, const QString &)));
 }
 
 /* at that point, the streaming shall be started */
 void CameraGrabber::onStart()
 {
-    camera->setCaptureMode(QCamera::CaptureVideo);
-    camera->setViewfinder(videoSurface);
     camera->start();
 }
 
@@ -132,15 +167,20 @@ void CameraGrabber::onStop()
 /* inverse of onOpen(...) */
 void CameraGrabber::onClose()
 {
+    if(videoSurface)
+    {
+        delete videoSurface;
+        videoSurface = nullptr;
+    }
     if(camera)
     {
         delete camera;
         camera = nullptr;
     }
-    if(videoSurface)
+    if(session)
     {
-        delete videoSurface;
-        videoSurface = nullptr;
+        delete session;
+        session = nullptr;
     }
 }
 // LITERAL_INCLUDE_END_3
diff --git a/nexxT/tests/src/CameraGrabber.hpp b/nexxT/tests/src/CameraGrabber.hpp
index b203c5e..010b3c1 100644
--- a/nexxT/tests/src/CameraGrabber.hpp
+++ b/nexxT/tests/src/CameraGrabber.hpp
@@ -10,10 +10,11 @@
 
 #include <QtCore/QObject>
 #include <QtMultimedia/QCamera>
+#include <QtMultimedia/QMediaCaptureSession>
 #include "VideoGrabber.hpp"
-#include "Filters.hpp"
-#include "Ports.hpp"
-#include "NexxTPlugins.hpp"
+#include "nexxT/Filters.hpp"
+#include "nexxT/Ports.hpp"
+#include "nexxT/NexxTPlugins.hpp"
 #include "ImageFormat.h"
 
 using namespace nexxT;
@@ -36,6 +37,8 @@ class CameraGrabber : public Filter
     QCamera *camera;
     /* the video surface needed to actually grab QImages from the QCamera */
     VideoGrabber *videoSurface;
+    /* the capture session */
+    QMediaCaptureSession *session;
 
 public:
     /* The following line is needed to declare a nexxT plugin. Note that also the following
@@ -58,7 +61,7 @@ public slots:
     /* Slot called from the videoSurface instance when a new image arrives */
     void newImage(const QImage &img);
     /* Slot called by the QtMultimedia framework to detect errors (they will happen!) */
-    void onStateChanged(QCamera::State state);
+    void onErrorOccurred(QCamera::Error error, const QString &errString);
 
 protected: // folowing functions are overloaded from the nexxT::Filter API
     void onOpen();
diff --git a/nexxT/tests/src/Plugins.cpp b/nexxT/tests/src/Plugins.cpp
index 1342f74..9008503 100644
--- a/nexxT/tests/src/Plugins.cpp
+++ b/nexxT/tests/src/Plugins.cpp
@@ -4,7 +4,9 @@
  *
  * THE PROGRAM IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND.
  */
+#include <QtGlobal>
 
+#if QT_VERSION < 0x060000
 #include "AviFilePlayback.hpp"
 #include "CameraGrabber.hpp"
 #include "SimpleSource.hpp"
@@ -16,3 +18,20 @@ NEXXT_PLUGIN_ADD_FILTER(CameraGrabber)
 NEXXT_PLUGIN_ADD_FILTER(SimpleSource)
 NEXXT_PLUGIN_ADD_FILTER(TestExceptionFilter)
 NEXXT_PLUGIN_DEFINE_FINISH()
+
+#else
+/* QT 6.1.x does not support multimedia */
+
+#include "AviFilePlayback.hpp"
+#include "CameraGrabber.hpp"
+#include "SimpleSource.hpp"
+#include "TestExceptionFilter.hpp"
+
+NEXXT_PLUGIN_DEFINE_START()
+NEXXT_PLUGIN_ADD_FILTER(VideoPlaybackDevice)
+NEXXT_PLUGIN_ADD_FILTER(CameraGrabber)
+NEXXT_PLUGIN_ADD_FILTER(SimpleSource)
+NEXXT_PLUGIN_ADD_FILTER(TestExceptionFilter)
+NEXXT_PLUGIN_DEFINE_FINISH()
+
+#endif
\ No newline at end of file
diff --git a/nexxT/tests/src/SConscript.py b/nexxT/tests/src/SConscript.py
index e0a0ac3..153175e 100644
--- a/nexxT/tests/src/SConscript.py
+++ b/nexxT/tests/src/SConscript.py
@@ -4,27 +4,47 @@
 # THE PROGRAM IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND.
 #
 
-import sysconfig
+import os
 
 Import("env")
 
 env = env.Clone()
-env.EnableQt5Modules(['QtCore', "QtMultimedia", "QtGui"])
-srcDir = Dir(".").srcnode()
-
-env.Append(CPPPATH=["../../src", "."],
-           LIBPATH=["../../src"],
-           LIBS=["nexxT"])
-
-plugin = env.SharedLibrary("test_plugins", env.RegisterSources(Split("""
-    SimpleSource.cpp
-    AviFilePlayback.cpp
-    TestExceptionFilter.cpp
-    Plugins.cpp
-    CameraGrabber.cpp
-    VideoGrabber.cpp
-""")))
-env.RegisterTargets(plugin)
+if os.environ.get("PYSIDEVERSION", "6") in "52":
+    env.EnableQt5Modules(['QtCore', "QtMultimedia", "QtGui"])
+    srcDir = Dir(".").srcnode()
+
+    env.Append(CPPPATH=["../../src", "."],
+               LIBPATH=["../../src"],
+               LIBS=["nexxT"])
+
+    plugin = env.SharedLibrary("test_plugins", env.RegisterSources(Split("""
+        SimpleSource.cpp
+        AviFilePlayback.cpp
+        TestExceptionFilter.cpp
+        Plugins.cpp
+        CameraGrabber.cpp
+        VideoGrabber.cpp
+    """)))
+    env.RegisterTargets(plugin)
+elif os.environ.get("PYSIDEVERSION", "6") == "6":
+    env.EnableQt6Modules(['QtCore', "QtGui", "QtMultimedia"])
+    srcDir = Dir(".").srcnode()
+
+    env.Append(CPPPATH=[srcDir.Dir("../../include"), "."],
+               LIBPATH=["../../src"],
+               LIBS=["nexxT"])
+
+    plugin = env.SharedLibrary("test_plugins", env.RegisterSources(Split("""
+        SimpleSource.cpp
+        AviFilePlayback.cpp
+        TestExceptionFilter.cpp
+        Plugins.cpp
+        VideoGrabber.cpp
+        CameraGrabber.cpp
+    """)))
+    env.RegisterTargets(plugin)
+else:
+    raise RuntimeError("invalid env variable PYSIDEVERSION=%s" % os.environ["PYSIDEVERSION"])
 
 installed = env.Install(srcDir.Dir("..").Dir("binary").Dir(env.subst("$deploy_platform")).Dir(env.subst("$variant")).abspath, plugin)
 env.RegisterTargets(installed)
diff --git a/nexxT/tests/src/SimpleSource.cpp b/nexxT/tests/src/SimpleSource.cpp
index ecd6a93..f795bc9 100644
--- a/nexxT/tests/src/SimpleSource.cpp
+++ b/nexxT/tests/src/SimpleSource.cpp
@@ -6,9 +6,9 @@
  */
 
 #include "SimpleSource.hpp"
-#include "PropertyCollection.hpp"
-#include "DataSamples.hpp"
-#include "Logger.hpp"
+#include "nexxT/PropertyCollection.hpp"
+#include "nexxT/DataSamples.hpp"
+#include "nexxT/Logger.hpp"
 #include <chrono>
 
 SimpleSource::SimpleSource(nexxT::BaseFilterEnvironment *env) : 
diff --git a/nexxT/tests/src/SimpleSource.hpp b/nexxT/tests/src/SimpleSource.hpp
index 8cc219c..12e85a1 100644
--- a/nexxT/tests/src/SimpleSource.hpp
+++ b/nexxT/tests/src/SimpleSource.hpp
@@ -8,7 +8,7 @@
 #ifndef SIMPLE_SOURCE_HPP
 #define SIMPLE_SOURCE_HPP
 
-#include "NexxTPlugins.hpp"
+#include "nexxT/NexxTPlugins.hpp"
 #include <QtCore/QTimer>
 #include <cstdint>
 
diff --git a/nexxT/tests/src/TestExceptionFilter.cpp b/nexxT/tests/src/TestExceptionFilter.cpp
index cea3446..fad0729 100644
--- a/nexxT/tests/src/TestExceptionFilter.cpp
+++ b/nexxT/tests/src/TestExceptionFilter.cpp
@@ -6,7 +6,8 @@
  */
 
 #include "TestExceptionFilter.hpp"
-#include "PropertyCollection.hpp"
+#include "nexxT/PropertyCollection.hpp"
+#include <stdexcept>
 
 TestExceptionFilter::TestExceptionFilter(BaseFilterEnvironment *env)
     : Filter(false, false, env)
diff --git a/nexxT/tests/src/TestExceptionFilter.hpp b/nexxT/tests/src/TestExceptionFilter.hpp
index d4ca632..a299e10 100644
--- a/nexxT/tests/src/TestExceptionFilter.hpp
+++ b/nexxT/tests/src/TestExceptionFilter.hpp
@@ -8,9 +8,9 @@
 #ifndef TESTEXCEPTIONFILTER_HPP
 #define TESTEXCEPTIONFILTER_HPP
 
-#include "Filters.hpp"
-#include "Ports.hpp"
-#include "NexxTPlugins.hpp"
+#include "nexxT/Filters.hpp"
+#include "nexxT/Ports.hpp"
+#include "nexxT/NexxTPlugins.hpp"
 
 using namespace nexxT;
 
diff --git a/nexxT/tests/src/VideoGrabber.cpp b/nexxT/tests/src/VideoGrabber.cpp
index 31d6036..1861d1b 100644
--- a/nexxT/tests/src/VideoGrabber.cpp
+++ b/nexxT/tests/src/VideoGrabber.cpp
@@ -5,94 +5,25 @@
  * THE PROGRAM IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND.
  */
 #include "VideoGrabber.hpp"
-#include "Logger.hpp"
+#include "nexxT/Logger.hpp"
+#include <QtGui/QImage>
+#include <QtMultimedia/QVideoFrame>
 
 using namespace nexxT;
 
-VideoGrabber::VideoGrabber(QObject *parent) : QAbstractVideoSurface(parent) {}
+VideoGrabber::VideoGrabber(QObject *parent) : QVideoSink(parent) {
+    connect(this, SIGNAL(videoFrameChanged(const QVideoFrame &)),
+            this, SLOT(videoFrameChanged(const QVideoFrame &)));
+}
 
 VideoGrabber::~VideoGrabber()
 {
     NEXXT_LOG_INTERNAL("VideoGrabber::~VideoGrabber (qt message)");
 }
 
-
-QList<QVideoFrame::PixelFormat> VideoGrabber::supportedPixelFormats(QAbstractVideoBuffer::HandleType handleType) const
-{
-    NEXXT_LOG_INTERNAL("QVideoSurfaceFormat::supportedPixelFormats called");
-
-    Q_UNUSED(handleType);
-    return QList<QVideoFrame::PixelFormat>()
-        << QVideoFrame::Format_ARGB32
-        << QVideoFrame::Format_ARGB32_Premultiplied
-        << QVideoFrame::Format_RGB32
-        << QVideoFrame::Format_RGB24
-        << QVideoFrame::Format_RGB565
-        << QVideoFrame::Format_RGB555
-        << QVideoFrame::Format_ARGB8565_Premultiplied
-        << QVideoFrame::Format_BGRA32
-        << QVideoFrame::Format_BGRA32_Premultiplied
-        << QVideoFrame::Format_BGR32
-        << QVideoFrame::Format_BGR24
-        << QVideoFrame::Format_BGR565
-        << QVideoFrame::Format_BGR555
-        << QVideoFrame::Format_BGRA5658_Premultiplied
-        << QVideoFrame::Format_AYUV444
-        << QVideoFrame::Format_AYUV444_Premultiplied
-        << QVideoFrame::Format_YUV444
-        << QVideoFrame::Format_YUV420P
-        << QVideoFrame::Format_YV12
-        << QVideoFrame::Format_UYVY
-        << QVideoFrame::Format_YUYV
-        << QVideoFrame::Format_NV12
-        << QVideoFrame::Format_NV21
-        << QVideoFrame::Format_IMC1
-        << QVideoFrame::Format_IMC2
-        << QVideoFrame::Format_IMC3
-        << QVideoFrame::Format_IMC4
-        << QVideoFrame::Format_Y8
-        << QVideoFrame::Format_Y16
-        << QVideoFrame::Format_Jpeg
-        << QVideoFrame::Format_CameraRaw
-        << QVideoFrame::Format_AdobeDng;
-}
-
-bool VideoGrabber::isFormatSupported(const QVideoSurfaceFormat &format) const
-{
-    NEXXT_LOG_INTERNAL("QVideoSurfaceFormat::isFormatSupported called");
-
-    const QImage::Format imageFormat = QVideoFrame::imageFormatFromPixelFormat(format.pixelFormat());
-    const QSize size = format.frameSize();
-
-    return imageFormat != QImage::Format_Invalid
-            && !size.isEmpty()
-            && format.handleType() == QAbstractVideoBuffer::NoHandle;
-}
-
-bool VideoGrabber::start(const QVideoSurfaceFormat &format)
+void VideoGrabber::videoFrameChanged(const QVideoFrame &frame)
 {
-    NEXXT_LOG_INTERNAL("QVideoSurfaceFormat::start called");
-
-    return QAbstractVideoSurface::start(format);
-}
-
-void VideoGrabber::stop()
-{
-    NEXXT_LOG_INTERNAL("QVideoSurfaceFormat::stop called");
-    QAbstractVideoSurface::stop();
-}
-
-bool VideoGrabber::present(const QVideoFrame &_frame)
-{
-    QImage img = _frame.image();
-    if(!img.isNull())
-    {
-        emit newImage(img);
-        return true;
-    } else
-    {
-        return false;
-    }
-}
-
-
+    NEXXT_LOG_DEBUG("new frame");
+    QImage img = frame.toImage();
+    emit newImage(img);
+}
\ No newline at end of file
diff --git a/nexxT/tests/src/VideoGrabber.hpp b/nexxT/tests/src/VideoGrabber.hpp
index 7051fc0..915c9a4 100644
--- a/nexxT/tests/src/VideoGrabber.hpp
+++ b/nexxT/tests/src/VideoGrabber.hpp
@@ -9,27 +9,23 @@
 #define NEXXT_VIDEO_GRABBER_HPP
 
 #include <QtCore/QObject>
-#include <QtMultimedia/QAbstractVideoSurface>
-#include <QtMultimedia/QVideoSurfaceFormat>
-#include "NexxTLinkage.hpp"
+#include <QtMultimedia/QVideoSink>
+#include "nexxT/NexxTLinkage.hpp"
 
 /* It would be lovely to do this stuff in python, but atm the binding is broken in PySide2
    See https://bugreports.qt.io/browse/PYSIDE-794
 */
-class VideoGrabber : public QAbstractVideoSurface
+class VideoGrabber : public QVideoSink
 {
     Q_OBJECT
 
 signals:
     void newImage(const QImage &);
+private slots:
+    void videoFrameChanged(const QVideoFrame &);
 public:
     VideoGrabber(QObject *parent);
     virtual ~VideoGrabber();
-    virtual QList<QVideoFrame::PixelFormat> supportedPixelFormats(QAbstractVideoBuffer::HandleType handleType) const;
-    virtual bool isFormatSupported(const QVideoSurfaceFormat &format) const;
-    virtual bool start(const QVideoSurfaceFormat &format);
-    virtual void stop();
-    virtual bool present(const QVideoFrame &_frame);
 };
 
 #endif
diff --git a/workspace/pytest_nexxT.sh b/workspace/pytest_nexxT.sh
index 3a6a28c..d7769ea 100644
--- a/workspace/pytest_nexxT.sh
+++ b/workspace/pytest_nexxT.sh
@@ -8,9 +8,9 @@ fi
 ADD_FLAGS=""
 
 # release variant
-                      "$PYTEST" -m     "gui" $ADD_FLAGS              --cov=nexxT.core --cov=nexxT.interface --cov=nexxT.services --cov=nexxT.filters --cov-report html --forked ../nexxT/tests
-                      "$PYTEST" -m "not gui" $ADD_FLAGS --cov-append --cov=nexxT.core --cov=nexxT.interface --cov=nexxT.services --cov=nexxT.filters --cov-report html ../nexxT/tests
+                      "$PYTEST" -m     "gui" $ADD_FLAGS              --cov=../nexxT/core --cov=../nexxT/interface --cov=../nexxT/services --cov=../nexxT/filters --cov-report html --forked ../nexxT/tests
+                      "$PYTEST" -m "not gui" $ADD_FLAGS --cov-append --cov=../nexxT/core --cov=../nexxT/interface --cov=../nexxT/services --cov=../nexxT/filters --cov-report html ../nexxT/tests
 # other variants
-NEXXT_VARIANT=nonopt  "$PYTEST" -m "not gui" $ADD_FLAGS --cov-append --cov=nexxT.core --cov=nexxT.interface --cov=nexxT.services --cov=nexxT.filters --cov-report html ../nexxT/tests
-NEXXT_DISABLE_CIMPL=1 "$PYTEST" -m "not gui" $ADD_FLAGS --cov-append --cov=nexxT.core --cov=nexxT.interface --cov=nexxT.services --cov=nexxT.filters --cov-report html ../nexxT/tests
+NEXXT_VARIANT=nonopt  "$PYTEST" -m "not gui" $ADD_FLAGS --cov-append --cov=../nexxT/core --cov=../nexxT/interface --cov=../nexxT/services --cov=../nexxT/filters --cov-report html ../nexxT/tests
+NEXXT_DISABLE_CIMPL=1 "$PYTEST" -m "not gui" $ADD_FLAGS --cov-append --cov=../nexxT/core --cov=../nexxT/interface --cov=../nexxT/services --cov=../nexxT/filters --cov-report html ../nexxT/tests
 
diff --git a/workspace/sconstools3/qt6/test/basic/CPPPATH/CPPPATH-appended/SConscript-after b/workspace/sconstools3/qt6/test/basic/CPPPATH/CPPPATH-appended/SConscript-after
new file mode 100644
index 0000000..9a0eb95
--- /dev/null
+++ b/workspace/sconstools3/qt6/test/basic/CPPPATH/CPPPATH-appended/SConscript-after
@@ -0,0 +1,6 @@
+
+Import("qtEnv")
+qtEnv.Append(CPPPATH=['./local_include'])
+qtEnv.EnableQt5Modules(['QtCore'])
+qtEnv.Program(target = 'aaa', source = 'aaa.cpp')
+
diff --git a/workspace/sconstools3/qt6/test/basic/CPPPATH/CPPPATH-appended/SConscript-before b/workspace/sconstools3/qt6/test/basic/CPPPATH/CPPPATH-appended/SConscript-before
new file mode 100644
index 0000000..d5677bb
--- /dev/null
+++ b/workspace/sconstools3/qt6/test/basic/CPPPATH/CPPPATH-appended/SConscript-before
@@ -0,0 +1,5 @@
+
+Import("qtEnv")
+qtEnv.EnableQt5Modules(['QtCore'])
+qtEnv.Program(target = 'aaa', source = 'aaa.cpp')
+
diff --git a/workspace/sconstools3/qt6/test/basic/CPPPATH/CPPPATH-appended/image/SConscript b/workspace/sconstools3/qt6/test/basic/CPPPATH/CPPPATH-appended/image/SConscript
new file mode 100644
index 0000000..f392a40
--- /dev/null
+++ b/workspace/sconstools3/qt6/test/basic/CPPPATH/CPPPATH-appended/image/SConscript
@@ -0,0 +1,2 @@
+
+SConscript('sub/SConscript')
diff --git a/workspace/sconstools3/qt6/test/basic/CPPPATH/CPPPATH-appended/image/SConstruct b/workspace/sconstools3/qt6/test/basic/CPPPATH/CPPPATH-appended/image/SConstruct
new file mode 100644
index 0000000..d9d897d
--- /dev/null
+++ b/workspace/sconstools3/qt6/test/basic/CPPPATH/CPPPATH-appended/image/SConstruct
@@ -0,0 +1,6 @@
+import qtenv
+
+qtEnv = qtenv.createQtEnvironment()
+Export('qtEnv')
+SConscript('SConscript')
+
diff --git a/workspace/sconstools3/qt6/test/basic/CPPPATH/CPPPATH-appended/image/sub/aaa.cpp b/workspace/sconstools3/qt6/test/basic/CPPPATH/CPPPATH-appended/image/sub/aaa.cpp
new file mode 100644
index 0000000..36dc229
--- /dev/null
+++ b/workspace/sconstools3/qt6/test/basic/CPPPATH/CPPPATH-appended/image/sub/aaa.cpp
@@ -0,0 +1,13 @@
+#include "aaa.h"
+
+aaa::aaa()
+{
+  ;
+}
+
+int main() 
+{ 
+  aaa a; 
+  return 0; 
+}
+
diff --git a/workspace/sconstools3/qt6/test/basic/CPPPATH/CPPPATH-appended/image/sub/aaa.h b/workspace/sconstools3/qt6/test/basic/CPPPATH/CPPPATH-appended/image/sub/aaa.h
new file mode 100644
index 0000000..f397b48
--- /dev/null
+++ b/workspace/sconstools3/qt6/test/basic/CPPPATH/CPPPATH-appended/image/sub/aaa.h
@@ -0,0 +1,12 @@
+#include <QObject>
+#include "local_include.h"
+
+class aaa : public QObject
+{
+  Q_OBJECT
+public:
+
+  aaa();
+};
+
+
diff --git a/workspace/sconstools3/qt6/test/basic/CPPPATH/CPPPATH-appended/image/sub/local_include/local_include.h b/workspace/sconstools3/qt6/test/basic/CPPPATH/CPPPATH-appended/image/sub/local_include/local_include.h
new file mode 100644
index 0000000..b427f9a
--- /dev/null
+++ b/workspace/sconstools3/qt6/test/basic/CPPPATH/CPPPATH-appended/image/sub/local_include/local_include.h
@@ -0,0 +1,3 @@
+
+/* empty; just needs to be found */
+
diff --git a/workspace/sconstools3/qt6/test/basic/CPPPATH/CPPPATH-appended/sconstest-CPPPATH-appended-fail.py b/workspace/sconstools3/qt6/test/basic/CPPPATH/CPPPATH-appended/sconstest-CPPPATH-appended-fail.py
new file mode 100644
index 0000000..8c236b5
--- /dev/null
+++ b/workspace/sconstools3/qt6/test/basic/CPPPATH/CPPPATH-appended/sconstest-CPPPATH-appended-fail.py
@@ -0,0 +1,56 @@
+#!/usr/bin/env python
+#
+# Copyright (c) 2001-2010,2011,2012 The SCons Foundation
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+
+"""
+Test that an appended relative CPPPATH works with generated files.
+
+This is basically the same as CPPPATH.py, but the include path
+is env.Append-ed and everything goes into sub directory "sub".
+The SConscript does not add the necessary path, such that
+the compile run actually fails. Together with the second
+test, this demonstrates that the CPPPATH has an effect.
+
+"""
+
+import os.path
+
+import TestSCons
+
+test = TestSCons.TestSCons()
+
+test.dir_fixture('image')
+test.file_fixture('SConscript-before','sub/SConscript')
+test.file_fixture('../../../qtenv.py')
+test.file_fixture('../../../../__init__.py','site_scons/site_tools/qt5/__init__.py')
+test.run(status=2, stderr=None)
+
+test.pass_test()
+
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/workspace/sconstools3/qt6/test/basic/CPPPATH/CPPPATH-appended/sconstest-CPPPATH-appended.py b/workspace/sconstools3/qt6/test/basic/CPPPATH/CPPPATH-appended/sconstest-CPPPATH-appended.py
new file mode 100644
index 0000000..3d3dd7c
--- /dev/null
+++ b/workspace/sconstools3/qt6/test/basic/CPPPATH/CPPPATH-appended/sconstest-CPPPATH-appended.py
@@ -0,0 +1,54 @@
+#!/usr/bin/env python
+#
+# Copyright (c) 2001-2010,2011,2012 The SCons Foundation
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+
+"""
+Test that an appended relative CPPPATH works with generated files.
+
+This is basically the same as CPPPATH.py, but the include path
+is env.Append-ed and everything goes into sub directory "sub".
+In the SConscript we really add the necessary path, such that
+the compile run is successful. See also the accompanying test
+that is supposed to fail.
+
+"""
+
+import TestSCons
+
+test = TestSCons.TestSCons()
+
+test.dir_fixture('image')
+test.file_fixture('SConscript-after','sub/SConscript')
+test.file_fixture('../../../qtenv.py')
+test.file_fixture('../../../../__init__.py','site_scons/site_tools/qt5/__init__.py')
+test.run(stderr=None)
+
+test.pass_test()
+
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/workspace/sconstools3/qt6/test/basic/CPPPATH/CPPPATH/SConscript-after b/workspace/sconstools3/qt6/test/basic/CPPPATH/CPPPATH/SConscript-after
new file mode 100644
index 0000000..43b0b91
--- /dev/null
+++ b/workspace/sconstools3/qt6/test/basic/CPPPATH/CPPPATH/SConscript-after
@@ -0,0 +1,3 @@
+Import("qtEnv")
+qtEnv.EnableQt5Modules(['QtCore'])
+qtEnv.Program(target = 'aaa', source = 'aaa.cpp', CPPPATH=['$CPPPATH', './local_include'])
diff --git a/workspace/sconstools3/qt6/test/basic/CPPPATH/CPPPATH/SConscript-before b/workspace/sconstools3/qt6/test/basic/CPPPATH/CPPPATH/SConscript-before
new file mode 100644
index 0000000..95ff8a3
--- /dev/null
+++ b/workspace/sconstools3/qt6/test/basic/CPPPATH/CPPPATH/SConscript-before
@@ -0,0 +1,3 @@
+Import("qtEnv")
+qtEnv.EnableQt5Modules(['QtCore'])
+qtEnv.Program(target = 'aaa', source = 'aaa.cpp')
diff --git a/workspace/sconstools3/qt6/test/basic/CPPPATH/CPPPATH/image/SConstruct b/workspace/sconstools3/qt6/test/basic/CPPPATH/CPPPATH/image/SConstruct
new file mode 100644
index 0000000..d9d897d
--- /dev/null
+++ b/workspace/sconstools3/qt6/test/basic/CPPPATH/CPPPATH/image/SConstruct
@@ -0,0 +1,6 @@
+import qtenv
+
+qtEnv = qtenv.createQtEnvironment()
+Export('qtEnv')
+SConscript('SConscript')
+
diff --git a/workspace/sconstools3/qt6/test/basic/CPPPATH/CPPPATH/image/aaa.cpp b/workspace/sconstools3/qt6/test/basic/CPPPATH/CPPPATH/image/aaa.cpp
new file mode 100644
index 0000000..36dc229
--- /dev/null
+++ b/workspace/sconstools3/qt6/test/basic/CPPPATH/CPPPATH/image/aaa.cpp
@@ -0,0 +1,13 @@
+#include "aaa.h"
+
+aaa::aaa()
+{
+  ;
+}
+
+int main() 
+{ 
+  aaa a; 
+  return 0; 
+}
+
diff --git a/workspace/sconstools3/qt6/test/basic/CPPPATH/CPPPATH/image/aaa.h b/workspace/sconstools3/qt6/test/basic/CPPPATH/CPPPATH/image/aaa.h
new file mode 100644
index 0000000..f397b48
--- /dev/null
+++ b/workspace/sconstools3/qt6/test/basic/CPPPATH/CPPPATH/image/aaa.h
@@ -0,0 +1,12 @@
+#include <QObject>
+#include "local_include.h"
+
+class aaa : public QObject
+{
+  Q_OBJECT
+public:
+
+  aaa();
+};
+
+
diff --git a/workspace/sconstools3/qt6/test/basic/CPPPATH/CPPPATH/image/local_include/local_include.h b/workspace/sconstools3/qt6/test/basic/CPPPATH/CPPPATH/image/local_include/local_include.h
new file mode 100644
index 0000000..ad85bb2
--- /dev/null
+++ b/workspace/sconstools3/qt6/test/basic/CPPPATH/CPPPATH/image/local_include/local_include.h
@@ -0,0 +1,2 @@
+
+/* empty; just needs to be found */
diff --git a/workspace/sconstools3/qt6/test/basic/CPPPATH/CPPPATH/sconstest-CPPPATH-fail.py b/workspace/sconstools3/qt6/test/basic/CPPPATH/CPPPATH/sconstest-CPPPATH-fail.py
new file mode 100644
index 0000000..ad0ca4a
--- /dev/null
+++ b/workspace/sconstools3/qt6/test/basic/CPPPATH/CPPPATH/sconstest-CPPPATH-fail.py
@@ -0,0 +1,54 @@
+#!/usr/bin/env python
+#
+# Copyright (c) 2001-2010,2011,2012 The SCons Foundation
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+
+"""
+Test that CPPPATH works with generated files.
+
+The SConscript does not add the necessary path, such that
+the compile run actually fails. Together with the second
+test, this demonstrates that the CPPPATH has an effect.
+
+"""
+
+import os.path
+
+import TestSCons
+
+test = TestSCons.TestSCons()
+
+test.dir_fixture('image')
+test.file_fixture('SConscript-before','SConscript')
+test.file_fixture('../../../qtenv.py')
+test.file_fixture('../../../../__init__.py','site_scons/site_tools/qt5/__init__.py')
+test.run(status=2, stderr=None)
+
+test.pass_test()
+
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/workspace/sconstools3/qt6/test/basic/CPPPATH/CPPPATH/sconstest-CPPPATH.py b/workspace/sconstools3/qt6/test/basic/CPPPATH/CPPPATH/sconstest-CPPPATH.py
new file mode 100644
index 0000000..293933f
--- /dev/null
+++ b/workspace/sconstools3/qt6/test/basic/CPPPATH/CPPPATH/sconstest-CPPPATH.py
@@ -0,0 +1,53 @@
+#!/usr/bin/env python
+#
+# Copyright (c) 2001-2010,2011,2012 The SCons Foundation
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+
+"""
+Test that CPPPATH works with generated files.
+
+In the SConscript we really add the necessary path, such that
+the compile run is successful. See also the accompanying test
+that is supposed to fail.
+"""
+
+import os.path
+
+import TestSCons
+
+test = TestSCons.TestSCons()
+
+test.dir_fixture('image')
+test.file_fixture('SConscript-after','SConscript')
+test.file_fixture('../../../qtenv.py')
+test.file_fixture('../../../../__init__.py','site_scons/site_tools/qt5/__init__.py')
+test.run(stderr=None)
+
+test.pass_test()
+
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/workspace/sconstools3/qt6/test/basic/copied-env/image/MyFile.cpp b/workspace/sconstools3/qt6/test/basic/copied-env/image/MyFile.cpp
new file mode 100644
index 0000000..b5f3f8f
--- /dev/null
+++ b/workspace/sconstools3/qt6/test/basic/copied-env/image/MyFile.cpp
@@ -0,0 +1,5 @@
+
+#include "MyFile.h"
+void useit() {
+  aaa();
+}
diff --git a/workspace/sconstools3/qt6/test/basic/copied-env/image/MyFile.h b/workspace/sconstools3/qt6/test/basic/copied-env/image/MyFile.h
new file mode 100644
index 0000000..8b2a3f8
--- /dev/null
+++ b/workspace/sconstools3/qt6/test/basic/copied-env/image/MyFile.h
@@ -0,0 +1,2 @@
+
+void aaa(void) {};
diff --git a/workspace/sconstools3/qt6/test/basic/copied-env/image/SConscript b/workspace/sconstools3/qt6/test/basic/copied-env/image/SConscript
new file mode 100644
index 0000000..fc7668b
--- /dev/null
+++ b/workspace/sconstools3/qt6/test/basic/copied-env/image/SConscript
@@ -0,0 +1,11 @@
+Import("qtEnv")
+qtEnv.Append(CPPDEFINES = ['FOOBAZ'])
+
+copy = qtEnv.Clone()
+copy.Append(CPPDEFINES = ['MYLIB_IMPL'])
+copy.EnableQt5Modules(['QtCore'])
+
+copy.SharedLibrary(
+   target = 'MyLib',
+   source = ['MyFile.cpp']
+)
diff --git a/workspace/sconstools3/qt6/test/basic/copied-env/image/SConstruct b/workspace/sconstools3/qt6/test/basic/copied-env/image/SConstruct
new file mode 100644
index 0000000..d9d897d
--- /dev/null
+++ b/workspace/sconstools3/qt6/test/basic/copied-env/image/SConstruct
@@ -0,0 +1,6 @@
+import qtenv
+
+qtEnv = qtenv.createQtEnvironment()
+Export('qtEnv')
+SConscript('SConscript')
+
diff --git a/workspace/sconstools3/qt6/test/basic/copied-env/sconstest-copied-env.py b/workspace/sconstools3/qt6/test/basic/copied-env/sconstest-copied-env.py
new file mode 100644
index 0000000..54ebcf8
--- /dev/null
+++ b/workspace/sconstools3/qt6/test/basic/copied-env/sconstest-copied-env.py
@@ -0,0 +1,52 @@
+#!/usr/bin/env python
+#
+# Copyright (c) 2001-2010,2011,2012 The SCons Foundation
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+
+"""
+Test Qt with a copied construction environment.
+"""
+from __future__ import print_function
+import TestSCons
+
+test = TestSCons.TestSCons()
+test.dir_fixture("image")
+test.file_fixture('../../qtenv.py')
+test.file_fixture('../../../__init__.py','site_scons/site_tools/qt5/__init__.py')
+test.run()
+
+cpp_MyFile = [x for x in test.stdout().split('\n') if x.find('MyFile.cpp') != -1]
+
+for x in cpp_MyFile:
+	if ((x.find('MYLIB_IMPL') < 0) or (x.find('FOOBAZ') < 0)):
+	    print("Did not find MYLIB_IMPL and FOOBAZ on MyFile.cpp compilation line:")
+	    print(test.stdout())
+	    test.fail_test()
+
+test.pass_test()
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/workspace/sconstools3/qt6/test/basic/empty-env/image/SConstruct b/workspace/sconstools3/qt6/test/basic/empty-env/image/SConstruct
new file mode 100644
index 0000000..f7c3e64
--- /dev/null
+++ b/workspace/sconstools3/qt6/test/basic/empty-env/image/SConstruct
@@ -0,0 +1,4 @@
+import qtenv
+
+qtEnv = qtenv.createQtEnvironment()
+qtEnv.Program('main', 'main.cpp', CPPDEFINES=['FOO'], LIBS=[])
diff --git a/workspace/sconstools3/qt6/test/basic/empty-env/image/foo6.h b/workspace/sconstools3/qt6/test/basic/empty-env/image/foo6.h
new file mode 100644
index 0000000..734d7c7
--- /dev/null
+++ b/workspace/sconstools3/qt6/test/basic/empty-env/image/foo6.h
@@ -0,0 +1,8 @@
+#include <stdio.h>
+void
+foo6(void)
+{
+#ifdef  FOO
+    printf("qt/include/foo6.h\n");
+#endif
+}
diff --git a/workspace/sconstools3/qt6/test/basic/empty-env/image/main.cpp b/workspace/sconstools3/qt6/test/basic/empty-env/image/main.cpp
new file mode 100644
index 0000000..cedf3cb
--- /dev/null
+++ b/workspace/sconstools3/qt6/test/basic/empty-env/image/main.cpp
@@ -0,0 +1,3 @@
+
+#include "foo6.h"
+int main() { foo6(); return 0; }
diff --git a/workspace/sconstools3/qt6/test/basic/empty-env/sconstest-empty-env.py b/workspace/sconstools3/qt6/test/basic/empty-env/sconstest-empty-env.py
new file mode 100644
index 0000000..bc894b3
--- /dev/null
+++ b/workspace/sconstools3/qt6/test/basic/empty-env/sconstest-empty-env.py
@@ -0,0 +1,47 @@
+#!/usr/bin/env python
+#
+# Copyright (c) 2001-2010,2011,2012 The SCons Foundation
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+"""
+Test Qt creation from a copied empty environment.
+"""
+
+import TestSCons
+
+test = TestSCons.TestSCons()
+test.dir_fixture('image')
+test.file_fixture('../../qtenv.py')
+test.file_fixture('../../../__init__.py','site_scons/site_tools/qt5/__init__.py')
+test.run()
+
+test.run(program = test.workpath('main' + TestSCons._exe),
+         stderr = None,
+         stdout = 'qt/include/foo6.h\n')
+
+test.pass_test()
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/workspace/sconstools3/qt6/test/basic/manual/image/SConscript b/workspace/sconstools3/qt6/test/basic/manual/image/SConscript
new file mode 100644
index 0000000..085947e
--- /dev/null
+++ b/workspace/sconstools3/qt6/test/basic/manual/image/SConscript
@@ -0,0 +1,20 @@
+Import("qtEnv")
+sources = ['aaa.cpp', 'bbb.cpp', 'ddd.cpp', 'eee.cpp', 'main.cpp']
+
+qtEnv.EnableQt5Modules(['QtCore','QtGui'])
+
+# normal invocation
+sources.append(qtEnv.Moc5('include/aaa.h'))
+sources.append(qtEnv.Moc5('ui/ccc.h'))
+qtEnv.Moc5('bbb.cpp')
+qtEnv.Uic5('ui/ccc.ui')
+
+# manual target specification
+sources.append(qtEnv.ExplicitMoc5('moc-ddd.cpp','include/ddd.h'))
+qtEnv.ExplicitMoc5('moc_eee.cpp','eee.cpp')
+qtEnv.ExplicitUic5('include/uic_fff.hpp','ui/fff.ui')
+
+qtEnv.Program(target='aaa',
+              source=sources,
+              CPPPATH=['$CPPPATH', './include'],
+              QT5_AUTOSCAN=0)
diff --git a/workspace/sconstools3/qt6/test/basic/manual/image/SConstruct b/workspace/sconstools3/qt6/test/basic/manual/image/SConstruct
new file mode 100644
index 0000000..d9d897d
--- /dev/null
+++ b/workspace/sconstools3/qt6/test/basic/manual/image/SConstruct
@@ -0,0 +1,6 @@
+import qtenv
+
+qtEnv = qtenv.createQtEnvironment()
+Export('qtEnv')
+SConscript('SConscript')
+
diff --git a/workspace/sconstools3/qt6/test/basic/manual/image/aaa.cpp b/workspace/sconstools3/qt6/test/basic/manual/image/aaa.cpp
new file mode 100644
index 0000000..cbd37c1
--- /dev/null
+++ b/workspace/sconstools3/qt6/test/basic/manual/image/aaa.cpp
@@ -0,0 +1,2 @@
+
+#include "aaa.h"
diff --git a/workspace/sconstools3/qt6/test/basic/manual/image/bbb.cpp b/workspace/sconstools3/qt6/test/basic/manual/image/bbb.cpp
new file mode 100644
index 0000000..159cc07
--- /dev/null
+++ b/workspace/sconstools3/qt6/test/basic/manual/image/bbb.cpp
@@ -0,0 +1,18 @@
+#include "bbb.h"
+
+#include <QObject>
+
+class bbb : public QObject
+{
+  Q_OBJECT
+
+public:
+  bbb() {};
+};
+
+#include "bbb.moc"
+
+void b_dummy()
+{
+  bbb b;
+}
diff --git a/workspace/sconstools3/qt6/test/basic/manual/image/bbb.h b/workspace/sconstools3/qt6/test/basic/manual/image/bbb.h
new file mode 100644
index 0000000..c3ec38e
--- /dev/null
+++ b/workspace/sconstools3/qt6/test/basic/manual/image/bbb.h
@@ -0,0 +1,2 @@
+
+extern void b_dummy(void);
diff --git a/workspace/sconstools3/qt6/test/basic/manual/image/ddd.cpp b/workspace/sconstools3/qt6/test/basic/manual/image/ddd.cpp
new file mode 100644
index 0000000..cd59f26
--- /dev/null
+++ b/workspace/sconstools3/qt6/test/basic/manual/image/ddd.cpp
@@ -0,0 +1,2 @@
+
+#include "ddd.h"
diff --git a/workspace/sconstools3/qt6/test/basic/manual/image/eee.cpp b/workspace/sconstools3/qt6/test/basic/manual/image/eee.cpp
new file mode 100644
index 0000000..38cb3e3
--- /dev/null
+++ b/workspace/sconstools3/qt6/test/basic/manual/image/eee.cpp
@@ -0,0 +1,16 @@
+#include <QObject>
+
+class eee : public QObject
+{
+  Q_OBJECT
+
+public:
+  eee() {};
+};
+
+#include "moc_eee.cpp"
+
+void e_dummy()
+{
+  eee e;
+}
diff --git a/workspace/sconstools3/qt6/test/basic/manual/image/eee.h b/workspace/sconstools3/qt6/test/basic/manual/image/eee.h
new file mode 100644
index 0000000..cb2fe43
--- /dev/null
+++ b/workspace/sconstools3/qt6/test/basic/manual/image/eee.h
@@ -0,0 +1,2 @@
+
+extern void e_dummy(void);
diff --git a/workspace/sconstools3/qt6/test/basic/manual/image/fff.cpp b/workspace/sconstools3/qt6/test/basic/manual/image/fff.cpp
new file mode 100644
index 0000000..9e5f80d
--- /dev/null
+++ b/workspace/sconstools3/qt6/test/basic/manual/image/fff.cpp
@@ -0,0 +1,1 @@
+#include "uic_fff.hpp"
diff --git a/workspace/sconstools3/qt6/test/basic/manual/image/main.cpp b/workspace/sconstools3/qt6/test/basic/manual/image/main.cpp
new file mode 100644
index 0000000..74af94a
--- /dev/null
+++ b/workspace/sconstools3/qt6/test/basic/manual/image/main.cpp
@@ -0,0 +1,17 @@
+
+#include "aaa.h"
+#include "bbb.h"
+#include "ui/ccc.h"
+#include "ddd.h"
+#include "eee.h"
+#include "uic_fff.hpp"
+
+int main() {
+  aaa a;
+  b_dummy();
+  ccc c;
+  ddd d;
+  e_dummy();
+
+  return 0;
+}
diff --git a/workspace/sconstools3/qt6/test/basic/manual/image/ui/ccc.h b/workspace/sconstools3/qt6/test/basic/manual/image/ui/ccc.h
new file mode 100644
index 0000000..073dd1a
--- /dev/null
+++ b/workspace/sconstools3/qt6/test/basic/manual/image/ui/ccc.h
@@ -0,0 +1,9 @@
+#include <QObject>
+
+class ccc : public QObject
+{
+  Q_OBJECT
+
+public:
+  ccc() {};
+};
diff --git a/workspace/sconstools3/qt6/test/basic/manual/image/ui/ccc.ui b/workspace/sconstools3/qt6/test/basic/manual/image/ui/ccc.ui
new file mode 100644
index 0000000..87fffd4
--- /dev/null
+++ b/workspace/sconstools3/qt6/test/basic/manual/image/ui/ccc.ui
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>Form</class>
+ <widget class="QWidget" name="Form">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>288</width>
+    <height>108</height>
+   </rect>
+  </property>
+  <property name="windowTitle">
+   <string>Form</string>
+  </property>
+ </widget>
+ <resources/>
+ <connections/>
+</ui>
diff --git a/workspace/sconstools3/qt6/test/basic/manual/image/ui/fff.ui b/workspace/sconstools3/qt6/test/basic/manual/image/ui/fff.ui
new file mode 100644
index 0000000..180d91f
--- /dev/null
+++ b/workspace/sconstools3/qt6/test/basic/manual/image/ui/fff.ui
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>Form2</class>
+ <widget class="QWidget" name="Form2">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>288</width>
+    <height>108</height>
+   </rect>
+  </property>
+  <property name="windowTitle">
+   <string>Form2</string>
+  </property>
+ </widget>
+ <resources/>
+ <connections/>
+</ui>
diff --git a/workspace/sconstools3/qt6/test/basic/manual/sconstest-manual.py b/workspace/sconstools3/qt6/test/basic/manual/sconstest-manual.py
new file mode 100644
index 0000000..8b30365
--- /dev/null
+++ b/workspace/sconstools3/qt6/test/basic/manual/sconstest-manual.py
@@ -0,0 +1,57 @@
+#!/usr/bin/env python
+#
+# Copyright (c) 2001-2010,2011,2012 The SCons Foundation
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+"""
+Test the manual QT builder calls.
+"""
+
+import TestSCons
+
+test = TestSCons.TestSCons()
+
+test.dir_fixture('image')
+test.file_fixture('../../qtenv.py')
+test.file_fixture('../../../__init__.py','site_scons/site_tools/qt5/__init__.py')
+
+test.run()
+
+# normal invocation
+test.must_exist(test.workpath('include', 'moc_aaa.cc'))
+test.must_exist(test.workpath('bbb.moc'))
+test.must_exist(test.workpath('ui', 'ccc.h'))
+test.must_exist(test.workpath('ui', 'moc_ccc.cc'))
+
+# manual target spec.
+test.must_exist(test.workpath('moc-ddd.cpp'))
+test.must_exist(test.workpath('moc_eee.cpp'))
+test.must_exist(test.workpath('include', 'uic_fff.hpp'))
+test.must_exist(test.workpath('fff.cpp'))
+
+test.pass_test()
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/workspace/sconstools3/qt6/test/basic/moc-from-cpp/image/SConscript b/workspace/sconstools3/qt6/test/basic/moc-from-cpp/image/SConscript
new file mode 100644
index 0000000..571778e
--- /dev/null
+++ b/workspace/sconstools3/qt6/test/basic/moc-from-cpp/image/SConscript
@@ -0,0 +1,7 @@
+Import("qtEnv dup")
+
+qtEnv.EnableQt5Modules(['QtCore','QtGui'])
+
+if dup == 0: qtEnv.Append(CPPPATH=['.'])
+aaa_lib = qtEnv.StaticLibrary('aaa',['aaa.cpp','useit.cpp'])
+qtEnv.Alias('aaa_lib', aaa_lib)
diff --git a/workspace/sconstools3/qt6/test/basic/moc-from-cpp/image/SConstruct b/workspace/sconstools3/qt6/test/basic/moc-from-cpp/image/SConstruct
new file mode 100644
index 0000000..acb6a99
--- /dev/null
+++ b/workspace/sconstools3/qt6/test/basic/moc-from-cpp/image/SConstruct
@@ -0,0 +1,25 @@
+from __future__ import print_function
+import qtenv
+
+qtEnv = qtenv.createQtEnvironment()
+
+dup = 1
+if ARGUMENTS.get('variant_dir', 0):
+    if ARGUMENTS.get('chdir', 0):
+        SConscriptChdir(1)
+    else:
+        SConscriptChdir(0)
+    dup=int(ARGUMENTS.get('dup', 1))
+    if dup == 0:
+        builddir = 'build_dup0'
+        qtEnv['QT5_DEBUG'] = 1
+    else:
+        builddir = 'build'
+    VariantDir(builddir, '.', duplicate=dup)
+    print(builddir, dup)
+    sconscript = Dir(builddir).File('SConscript')
+else:
+    sconscript = File('SConscript')
+
+Export("qtEnv dup")
+SConscript( sconscript )
diff --git a/workspace/sconstools3/qt6/test/basic/moc-from-cpp/image/aaa.cpp b/workspace/sconstools3/qt6/test/basic/moc-from-cpp/image/aaa.cpp
new file mode 100644
index 0000000..07a28ef
--- /dev/null
+++ b/workspace/sconstools3/qt6/test/basic/moc-from-cpp/image/aaa.cpp
@@ -0,0 +1,18 @@
+#include "aaa.h"
+
+#include <QObject>
+
+class aaa : public QObject
+{
+  Q_OBJECT
+
+public:
+  aaa() {};
+};
+
+#include "aaa.moc"
+
+void dummy_a()
+{
+  aaa a;
+}
diff --git a/workspace/sconstools3/qt6/test/basic/moc-from-cpp/image/aaa.h b/workspace/sconstools3/qt6/test/basic/moc-from-cpp/image/aaa.h
new file mode 100644
index 0000000..8626f9f
--- /dev/null
+++ b/workspace/sconstools3/qt6/test/basic/moc-from-cpp/image/aaa.h
@@ -0,0 +1,2 @@
+
+extern void dummy_a(void);
diff --git a/workspace/sconstools3/qt6/test/basic/moc-from-cpp/image/useit.cpp b/workspace/sconstools3/qt6/test/basic/moc-from-cpp/image/useit.cpp
new file mode 100644
index 0000000..0942ec3
--- /dev/null
+++ b/workspace/sconstools3/qt6/test/basic/moc-from-cpp/image/useit.cpp
@@ -0,0 +1,4 @@
+#include "aaa.h"
+void useit() {
+  dummy_a();
+}
diff --git a/workspace/sconstools3/qt6/test/basic/moc-from-cpp/sconstest-moc-from-cpp.py b/workspace/sconstools3/qt6/test/basic/moc-from-cpp/sconstest-moc-from-cpp.py
new file mode 100644
index 0000000..feee87c
--- /dev/null
+++ b/workspace/sconstools3/qt6/test/basic/moc-from-cpp/sconstest-moc-from-cpp.py
@@ -0,0 +1,91 @@
+#!/usr/bin/env python
+#
+# Copyright (c) 2001-2010,2011,2012 The SCons Foundation
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+"""
+Create a moc file from a cpp file.
+"""
+
+import TestSCons
+
+test = TestSCons.TestSCons()
+test.dir_fixture('image')
+test.file_fixture('../../qtenv.py')
+test.file_fixture('../../../__init__.py','site_scons/site_tools/qt5/__init__.py')
+
+lib_aaa = 'aaa_lib' # Alias for the Library
+moc = 'aaa.moc'
+
+test.run(arguments=lib_aaa,
+         stderr=TestSCons.noisy_ar,
+         match=TestSCons.match_re_dotall)
+
+test.up_to_date(options = '-n', arguments = lib_aaa)
+
+test.write('aaa.cpp', r"""
+#include "aaa.h"
+
+#include <QObject>
+
+class aaa : public QObject
+{
+  Q_OBJECT
+
+public:
+  aaa() {};
+};
+
+#include "%s"
+
+// Using the class
+void dummy_a()
+{
+  aaa a;
+}
+""" % moc)
+
+test.not_up_to_date(options = '-n', arguments = moc)
+
+test.run(options = '-c', arguments = lib_aaa)
+
+test.run(arguments = "variant_dir=1 " + lib_aaa,
+         stderr=TestSCons.noisy_ar,
+         match=TestSCons.match_re_dotall)
+
+test.run(arguments = "variant_dir=1 chdir=1 " + lib_aaa)
+
+test.must_exist(test.workpath('build', moc))
+
+test.run(arguments = "variant_dir=1 dup=0 " + lib_aaa,
+         stderr=TestSCons.noisy_ar,
+         match=TestSCons.match_re_dotall)
+
+test.must_exist(test.workpath('build_dup0', moc))
+
+test.pass_test()
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/workspace/sconstools3/qt6/test/basic/moc-from-header-nocompile/image/SConscript b/workspace/sconstools3/qt6/test/basic/moc-from-header-nocompile/image/SConscript
new file mode 100644
index 0000000..4307a75
--- /dev/null
+++ b/workspace/sconstools3/qt6/test/basic/moc-from-header-nocompile/image/SConscript
@@ -0,0 +1,8 @@
+Import("qtEnv dup")
+
+qtEnv.EnableQt5Modules(['QtCore','QtGui'])
+
+if dup == 0: qtEnv.Append(CPPPATH=['.', '#build_dup0'])
+qtEnv.Program('aaa', 'aaa.cpp', 
+              QT5_MOCHPREFIX = 'moc_',
+              QT5_MOCHSUFFIX = '.cc')
diff --git a/workspace/sconstools3/qt6/test/basic/moc-from-header-nocompile/image/SConstruct b/workspace/sconstools3/qt6/test/basic/moc-from-header-nocompile/image/SConstruct
new file mode 100644
index 0000000..acb6a99
--- /dev/null
+++ b/workspace/sconstools3/qt6/test/basic/moc-from-header-nocompile/image/SConstruct
@@ -0,0 +1,25 @@
+from __future__ import print_function
+import qtenv
+
+qtEnv = qtenv.createQtEnvironment()
+
+dup = 1
+if ARGUMENTS.get('variant_dir', 0):
+    if ARGUMENTS.get('chdir', 0):
+        SConscriptChdir(1)
+    else:
+        SConscriptChdir(0)
+    dup=int(ARGUMENTS.get('dup', 1))
+    if dup == 0:
+        builddir = 'build_dup0'
+        qtEnv['QT5_DEBUG'] = 1
+    else:
+        builddir = 'build'
+    VariantDir(builddir, '.', duplicate=dup)
+    print(builddir, dup)
+    sconscript = Dir(builddir).File('SConscript')
+else:
+    sconscript = File('SConscript')
+
+Export("qtEnv dup")
+SConscript( sconscript )
diff --git a/workspace/sconstools3/qt6/test/basic/moc-from-header-nocompile/image/aaa.cpp b/workspace/sconstools3/qt6/test/basic/moc-from-header-nocompile/image/aaa.cpp
new file mode 100644
index 0000000..d8b45aa
--- /dev/null
+++ b/workspace/sconstools3/qt6/test/basic/moc-from-header-nocompile/image/aaa.cpp
@@ -0,0 +1,7 @@
+#include "moc_aaa.cc"
+
+int main() 
+{ 
+  aaa a; 
+  return 0; 
+}
diff --git a/workspace/sconstools3/qt6/test/basic/moc-from-header-nocompile/image/aaa.h b/workspace/sconstools3/qt6/test/basic/moc-from-header-nocompile/image/aaa.h
new file mode 100644
index 0000000..3438edd
--- /dev/null
+++ b/workspace/sconstools3/qt6/test/basic/moc-from-header-nocompile/image/aaa.h
@@ -0,0 +1,9 @@
+#include <QObject>
+
+class aaa : public QObject
+{
+  Q_OBJECT
+
+public:
+  aaa() {};
+};
diff --git a/workspace/sconstools3/qt6/test/basic/moc-from-header-nocompile/sconstest-moc-from-header-nocompile.py b/workspace/sconstools3/qt6/test/basic/moc-from-header-nocompile/sconstest-moc-from-header-nocompile.py
new file mode 100644
index 0000000..3e905a7
--- /dev/null
+++ b/workspace/sconstools3/qt6/test/basic/moc-from-header-nocompile/sconstest-moc-from-header-nocompile.py
@@ -0,0 +1,88 @@
+#!/usr/bin/env python
+#
+# Copyright (c) 2001-2010 The SCons Foundation
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+"""
+Create a moc file from a header file, but don't
+compile it to an Object because the moc'ed output gets directly
+included to the CXX file.
+"""
+
+import TestSCons
+
+test = TestSCons.TestSCons()
+test.dir_fixture('image')
+test.file_fixture('../../qtenv.py')
+test.file_fixture('../../../__init__.py','site_scons/site_tools/qt5/__init__.py')
+
+aaa_exe = 'aaa' + TestSCons._exe
+build_aaa_exe = test.workpath('build', aaa_exe)
+moc = 'moc_aaa.cc'
+moc_obj = 'moc_aaa' + TestSCons._obj
+
+test.run()
+
+# Ensure that the object file for the MOC output wasn't generated
+test.must_not_exist(moc_obj)
+
+test.up_to_date(options = '-n', arguments=aaa_exe)
+test.up_to_date(options = '-n', arguments=aaa_exe)
+
+test.write('aaa.h', r"""
+#include <QObject>
+
+// Introducing a change...
+class aaa : public QObject
+{
+  Q_OBJECT
+
+public:
+  aaa() {};
+};
+""")
+
+test.not_up_to_date(options='-n', arguments = moc)
+
+test.run(arguments = "variant_dir=1 " + build_aaa_exe)
+
+test.run(arguments = "variant_dir=1 chdir=1 " + build_aaa_exe)
+
+test.must_exist(test.workpath('build', moc))
+test.must_not_exist(test.workpath('build', moc_obj))
+
+test.run(options='-c')
+
+test.run(arguments = "variant_dir=1 chdir=1 dup=0 " +
+                     test.workpath('build_dup0', aaa_exe) )
+
+test.must_exist(test.workpath('build_dup0', moc))
+test.must_not_exist(moc_obj)
+test.must_not_exist(test.workpath('build_dup0', moc_obj))
+
+test.pass_test()
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/workspace/sconstools3/qt6/test/basic/moc-from-header/image/SConscript b/workspace/sconstools3/qt6/test/basic/moc-from-header/image/SConscript
new file mode 100644
index 0000000..4486ee8
--- /dev/null
+++ b/workspace/sconstools3/qt6/test/basic/moc-from-header/image/SConscript
@@ -0,0 +1,6 @@
+Import("qtEnv dup")
+
+qtEnv.EnableQt5Modules(['QtCore','QtGui'])
+
+if dup == 0: qtEnv.Append(CPPPATH=['.'])
+qtEnv.Program('aaa','aaa.cpp')
diff --git a/workspace/sconstools3/qt6/test/basic/moc-from-header/image/SConstruct b/workspace/sconstools3/qt6/test/basic/moc-from-header/image/SConstruct
new file mode 100644
index 0000000..acb6a99
--- /dev/null
+++ b/workspace/sconstools3/qt6/test/basic/moc-from-header/image/SConstruct
@@ -0,0 +1,25 @@
+from __future__ import print_function
+import qtenv
+
+qtEnv = qtenv.createQtEnvironment()
+
+dup = 1
+if ARGUMENTS.get('variant_dir', 0):
+    if ARGUMENTS.get('chdir', 0):
+        SConscriptChdir(1)
+    else:
+        SConscriptChdir(0)
+    dup=int(ARGUMENTS.get('dup', 1))
+    if dup == 0:
+        builddir = 'build_dup0'
+        qtEnv['QT5_DEBUG'] = 1
+    else:
+        builddir = 'build'
+    VariantDir(builddir, '.', duplicate=dup)
+    print(builddir, dup)
+    sconscript = Dir(builddir).File('SConscript')
+else:
+    sconscript = File('SConscript')
+
+Export("qtEnv dup")
+SConscript( sconscript )
diff --git a/workspace/sconstools3/qt6/test/basic/moc-from-header/image/aaa.cpp b/workspace/sconstools3/qt6/test/basic/moc-from-header/image/aaa.cpp
new file mode 100644
index 0000000..054d582
--- /dev/null
+++ b/workspace/sconstools3/qt6/test/basic/moc-from-header/image/aaa.cpp
@@ -0,0 +1,7 @@
+#include "aaa.h"
+
+int main() 
+{ 
+  aaa a; 
+  return 0; 
+}
diff --git a/workspace/sconstools3/qt6/test/basic/moc-from-header/image/aaa.h b/workspace/sconstools3/qt6/test/basic/moc-from-header/image/aaa.h
new file mode 100644
index 0000000..3438edd
--- /dev/null
+++ b/workspace/sconstools3/qt6/test/basic/moc-from-header/image/aaa.h
@@ -0,0 +1,9 @@
+#include <QObject>
+
+class aaa : public QObject
+{
+  Q_OBJECT
+
+public:
+  aaa() {};
+};
diff --git a/workspace/sconstools3/qt6/test/basic/moc-from-header/sconstest-moc-from-header.py b/workspace/sconstools3/qt6/test/basic/moc-from-header/sconstest-moc-from-header.py
new file mode 100644
index 0000000..ab5a0de
--- /dev/null
+++ b/workspace/sconstools3/qt6/test/basic/moc-from-header/sconstest-moc-from-header.py
@@ -0,0 +1,77 @@
+#!/usr/bin/env python
+#
+# Copyright (c) 2001-2010,2011,2012 The SCons Foundation
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+"""
+Create a moc file from a header file.
+"""
+
+import TestSCons
+
+test = TestSCons.TestSCons()
+test.dir_fixture('image')
+test.file_fixture('../../qtenv.py')
+test.file_fixture('../../../__init__.py','site_scons/site_tools/qt5/__init__.py')
+
+aaa_exe = 'aaa' + TestSCons._exe
+build_aaa_exe = test.workpath('build', aaa_exe)
+moc = 'moc_aaa.cc'
+
+test.run()
+
+test.up_to_date(options = '-n', arguments=aaa_exe)
+test.up_to_date(options = '-n', arguments=aaa_exe)
+
+test.write('aaa.h', r"""
+#include <QObject>
+
+// Introducing a change...
+class aaa : public QObject
+{
+  Q_OBJECT
+
+public:
+  aaa() {};
+};
+""")
+
+test.not_up_to_date(options='-n', arguments = moc)
+
+test.run(arguments = "variant_dir=1 " + build_aaa_exe)
+
+test.run(arguments = "variant_dir=1 chdir=1 " + build_aaa_exe)
+
+test.must_exist(test.workpath('build', moc))
+
+test.run(arguments = "variant_dir=1 chdir=1 dup=0 " +
+                     test.workpath('build_dup0', aaa_exe) )
+
+test.must_exist(['build_dup0', moc])
+
+test.pass_test()
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/workspace/sconstools3/qt6/test/basic/reentrant/image/SConscript b/workspace/sconstools3/qt6/test/basic/reentrant/image/SConscript
new file mode 100644
index 0000000..163e252
--- /dev/null
+++ b/workspace/sconstools3/qt6/test/basic/reentrant/image/SConscript
@@ -0,0 +1,4 @@
+Import("qtEnv")
+env = qtEnv.Clone(tool='qt5')
+
+env.Program('main', 'main.cpp', CPPDEFINES=['FOO'], LIBS=[])
diff --git a/workspace/sconstools3/qt6/test/basic/reentrant/image/SConstruct b/workspace/sconstools3/qt6/test/basic/reentrant/image/SConstruct
new file mode 100644
index 0000000..d9d897d
--- /dev/null
+++ b/workspace/sconstools3/qt6/test/basic/reentrant/image/SConstruct
@@ -0,0 +1,6 @@
+import qtenv
+
+qtEnv = qtenv.createQtEnvironment()
+Export('qtEnv')
+SConscript('SConscript')
+
diff --git a/workspace/sconstools3/qt6/test/basic/reentrant/image/foo5.h b/workspace/sconstools3/qt6/test/basic/reentrant/image/foo5.h
new file mode 100644
index 0000000..5ac79c8
--- /dev/null
+++ b/workspace/sconstools3/qt6/test/basic/reentrant/image/foo5.h
@@ -0,0 +1,8 @@
+#include <stdio.h>
+
+#ifdef  FOO
+void foo5(void)
+{
+    printf("qt/include/foo5.h\\n");
+}
+#endif
diff --git a/workspace/sconstools3/qt6/test/basic/reentrant/image/main.cpp b/workspace/sconstools3/qt6/test/basic/reentrant/image/main.cpp
new file mode 100644
index 0000000..0ea9300
--- /dev/null
+++ b/workspace/sconstools3/qt6/test/basic/reentrant/image/main.cpp
@@ -0,0 +1,7 @@
+#include "foo5.h"
+
+int main()
+{
+  foo5();
+  return 0;
+}
diff --git a/workspace/sconstools3/qt6/test/basic/reentrant/sconstest-reentrant.py b/workspace/sconstools3/qt6/test/basic/reentrant/sconstest-reentrant.py
new file mode 100644
index 0000000..97487f5
--- /dev/null
+++ b/workspace/sconstools3/qt6/test/basic/reentrant/sconstest-reentrant.py
@@ -0,0 +1,46 @@
+#!/usr/bin/env python
+#
+# Copyright (c) 2001-2010,2011,2012 The SCons Foundation
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+"""
+Test creation from a copied environment that already has QT variables.
+This makes sure the tool initialization is re-entrant.
+"""
+
+import TestSCons
+
+test = TestSCons.TestSCons()
+
+test.dir_fixture('image')
+test.file_fixture('../../qtenv.py')
+test.file_fixture('../../../__init__.py','site_scons/site_tools/qt5/__init__.py')
+
+test.run()
+
+test.pass_test()
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/workspace/sconstools3/qt6/test/basic/variantdir/image/MyForm.cpp b/workspace/sconstools3/qt6/test/basic/variantdir/image/MyForm.cpp
new file mode 100644
index 0000000..cdeb4a6
--- /dev/null
+++ b/workspace/sconstools3/qt6/test/basic/variantdir/image/MyForm.cpp
@@ -0,0 +1,14 @@
+#include "MyForm.h"
+
+#include <stdio.h>
+
+MyForm::MyForm(QWidget *parent) : QWidget(parent)
+{
+  ui.setupUi(this);
+}
+
+void MyForm::testSlot()
+{
+    printf("Hello World\n");
+}
+
diff --git a/workspace/sconstools3/qt6/test/basic/variantdir/image/MyForm.h b/workspace/sconstools3/qt6/test/basic/variantdir/image/MyForm.h
new file mode 100644
index 0000000..fa2d119
--- /dev/null
+++ b/workspace/sconstools3/qt6/test/basic/variantdir/image/MyForm.h
@@ -0,0 +1,18 @@
+#include "ui_anUiFile.h"
+
+#include <QtWidgets/QWidget>
+
+class MyForm : public QWidget
+{
+     Q_OBJECT
+
+ public:
+     MyForm(QWidget *parent = 0);
+
+ public slots:
+     void testSlot();
+
+ private:
+     Ui::Form ui;
+};
+
diff --git a/workspace/sconstools3/qt6/test/basic/variantdir/image/SConstruct b/workspace/sconstools3/qt6/test/basic/variantdir/image/SConstruct
new file mode 100644
index 0000000..56d7a18
--- /dev/null
+++ b/workspace/sconstools3/qt6/test/basic/variantdir/image/SConstruct
@@ -0,0 +1,12 @@
+import qtenv
+
+qtEnv = qtenv.createQtEnvironment()
+qtEnv.EnableQt5Modules(['QtCore','QtWidgets'])
+
+qtEnv.VariantDir('bld', '.')
+qtEnv.Uic5('bld/anUiFile.ui')
+qtEnv.Program('bld/test_realqt', ['bld/mocFromCpp.cpp',
+                                  'bld/mocFromH.cpp',
+                                  'bld/MyForm.cpp',
+                                  'bld/main.cpp'])
+
diff --git a/workspace/sconstools3/qt6/test/basic/variantdir/image/anUiFile.ui b/workspace/sconstools3/qt6/test/basic/variantdir/image/anUiFile.ui
new file mode 100644
index 0000000..87fffd4
--- /dev/null
+++ b/workspace/sconstools3/qt6/test/basic/variantdir/image/anUiFile.ui
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>Form</class>
+ <widget class="QWidget" name="Form">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>288</width>
+    <height>108</height>
+   </rect>
+  </property>
+  <property name="windowTitle">
+   <string>Form</string>
+  </property>
+ </widget>
+ <resources/>
+ <connections/>
+</ui>
diff --git a/workspace/sconstools3/qt6/test/basic/variantdir/image/main.cpp b/workspace/sconstools3/qt6/test/basic/variantdir/image/main.cpp
new file mode 100644
index 0000000..5212903
--- /dev/null
+++ b/workspace/sconstools3/qt6/test/basic/variantdir/image/main.cpp
@@ -0,0 +1,17 @@
+#include "mocFromCpp.h"
+#include "mocFromH.h"
+#include "MyForm.h"
+
+#include <QtWidgets/QApplication>
+#include <stdio.h>
+    
+int main(int argc, char **argv) {
+  QApplication app(argc, argv);
+  mocFromCpp();
+  mocFromH();
+  MyForm mywidget;
+  mywidget.testSlot();
+  printf("Hello World\n");
+  return 0;
+}
+
diff --git a/workspace/sconstools3/qt6/test/basic/variantdir/image/mocFromCpp.cpp b/workspace/sconstools3/qt6/test/basic/variantdir/image/mocFromCpp.cpp
new file mode 100644
index 0000000..1d5b560
--- /dev/null
+++ b/workspace/sconstools3/qt6/test/basic/variantdir/image/mocFromCpp.cpp
@@ -0,0 +1,16 @@
+#include <QObject>
+
+#include "mocFromCpp.h"
+
+class MyClass1 : public QObject {
+  Q_OBJECT
+  public:
+  MyClass1() : QObject() {};
+  public slots:
+  void myslot() {};
+};
+void mocFromCpp() {
+  MyClass1 myclass;
+}
+#include "mocFromCpp.moc"
+
diff --git a/workspace/sconstools3/qt6/test/basic/variantdir/image/mocFromCpp.h b/workspace/sconstools3/qt6/test/basic/variantdir/image/mocFromCpp.h
new file mode 100644
index 0000000..fc1e04c
--- /dev/null
+++ b/workspace/sconstools3/qt6/test/basic/variantdir/image/mocFromCpp.h
@@ -0,0 +1,2 @@
+void mocFromCpp();
+
diff --git a/workspace/sconstools3/qt6/test/basic/variantdir/image/mocFromH.cpp b/workspace/sconstools3/qt6/test/basic/variantdir/image/mocFromH.cpp
new file mode 100644
index 0000000..586aafb
--- /dev/null
+++ b/workspace/sconstools3/qt6/test/basic/variantdir/image/mocFromH.cpp
@@ -0,0 +1,8 @@
+#include "mocFromH.h"
+    
+MyClass2::MyClass2() : QObject() {}
+void MyClass2::myslot() {}
+void mocFromH() {
+  MyClass2 myclass;
+}
+
diff --git a/workspace/sconstools3/qt6/test/basic/variantdir/image/mocFromH.h b/workspace/sconstools3/qt6/test/basic/variantdir/image/mocFromH.h
new file mode 100644
index 0000000..48fdd7e
--- /dev/null
+++ b/workspace/sconstools3/qt6/test/basic/variantdir/image/mocFromH.h
@@ -0,0 +1,11 @@
+#include <QObject>
+
+class MyClass2 : public QObject {
+  Q_OBJECT;
+  public:
+  MyClass2();
+  public slots:
+  void myslot();
+};
+void mocFromH();
+
diff --git a/workspace/sconstools3/qt6/test/basic/variantdir/sconstest-installed.py b/workspace/sconstools3/qt6/test/basic/variantdir/sconstest-installed.py
new file mode 100644
index 0000000..7cef5c4
--- /dev/null
+++ b/workspace/sconstools3/qt6/test/basic/variantdir/sconstest-installed.py
@@ -0,0 +1,47 @@
+#!/usr/bin/env python
+#
+# Copyright (c) 2001-2010,2011,2012 The SCons Foundation
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+"""
+A simple test for VariantDir, in connection with basic
+automocing from cxx and header files and the Uic5 builder.
+"""
+
+import sys
+
+import TestSCons
+
+test = TestSCons.TestSCons()
+test.dir_fixture('image')
+test.file_fixture('../../qtenv.py')
+test.file_fixture('../../../__init__.py','site_scons/site_tools/qt5/__init__.py')
+
+test.run(arguments="bld/test_realqt" + TestSCons._exe)
+
+test.pass_test()
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/workspace/sconstools3/qt6/test/moc/auto/ccomment/image/SConscript b/workspace/sconstools3/qt6/test/moc/auto/ccomment/image/SConscript
new file mode 100644
index 0000000..d560985
--- /dev/null
+++ b/workspace/sconstools3/qt6/test/moc/auto/ccomment/image/SConscript
@@ -0,0 +1,9 @@
+Import("qtEnv")
+
+env = qtEnv.Clone()
+env['QT5_GOBBLECOMMENTS']=1
+env['QT5_DEBUG']=1
+env.EnableQt5Modules(['QtCore','QtWidgets'])
+
+env.Program('main', Glob('*.cpp'))
+
diff --git a/workspace/sconstools3/qt6/test/moc/auto/ccomment/image/SConstruct b/workspace/sconstools3/qt6/test/moc/auto/ccomment/image/SConstruct
new file mode 100644
index 0000000..9754f10
--- /dev/null
+++ b/workspace/sconstools3/qt6/test/moc/auto/ccomment/image/SConstruct
@@ -0,0 +1,6 @@
+import qtenv
+
+qtEnv = qtenv.createQtEnvironment()
+Export('qtEnv')
+
+SConscript('SConscript')
diff --git a/workspace/sconstools3/qt6/test/moc/auto/ccomment/image/main.cpp b/workspace/sconstools3/qt6/test/moc/auto/ccomment/image/main.cpp
new file mode 100644
index 0000000..6d8b713
--- /dev/null
+++ b/workspace/sconstools3/qt6/test/moc/auto/ccomment/image/main.cpp
@@ -0,0 +1,14 @@
+#include "mocFromCpp.h"
+#include "mocFromH.h"
+
+#include <QtWidgets/QApplication>
+#include <stdio.h>
+    
+int main(int argc, char **argv)
+{
+  QApplication app(argc, argv);
+  mocFromCpp();
+  mocFromH();
+  printf("Hello World\n");
+  return 0;
+}
diff --git a/workspace/sconstools3/qt6/test/moc/auto/ccomment/image/mocFromCpp.cpp b/workspace/sconstools3/qt6/test/moc/auto/ccomment/image/mocFromCpp.cpp
new file mode 100644
index 0000000..b4a24ec
--- /dev/null
+++ b/workspace/sconstools3/qt6/test/moc/auto/ccomment/image/mocFromCpp.cpp
@@ -0,0 +1,41 @@
+#include <QObject>
+
+#include "mocFromCpp.h"
+
+
+/*
+
+class MyClass1 : public QObject {
+  Q_OBJECT
+  public:
+  MyClass1() : QObject() {};
+  public slots:
+  void myslot() {};
+};
+void mocFromCpp() {
+  MyClass1 myclass;
+}
+#include "mocFromCpp.moc"
+
+*/
+
+// class MyClass1 : public QObject { Q_OBJECT; };
+
+class MyClass1
+{
+  // Q_OBJECT
+
+  /* and another Q_OBJECT but in
+   * a C comment,
+   * ... next Q_OBJECT
+   */
+
+public:
+  MyClass1() {};
+  void myslot() {};
+};
+
+void mocFromCpp()
+{
+  MyClass1 myclass;
+}
diff --git a/workspace/sconstools3/qt6/test/moc/auto/ccomment/image/mocFromCpp.h b/workspace/sconstools3/qt6/test/moc/auto/ccomment/image/mocFromCpp.h
new file mode 100644
index 0000000..fc1e04c
--- /dev/null
+++ b/workspace/sconstools3/qt6/test/moc/auto/ccomment/image/mocFromCpp.h
@@ -0,0 +1,2 @@
+void mocFromCpp();
+
diff --git a/workspace/sconstools3/qt6/test/moc/auto/ccomment/image/mocFromH.cpp b/workspace/sconstools3/qt6/test/moc/auto/ccomment/image/mocFromH.cpp
new file mode 100644
index 0000000..7cfb2be
--- /dev/null
+++ b/workspace/sconstools3/qt6/test/moc/auto/ccomment/image/mocFromH.cpp
@@ -0,0 +1,10 @@
+#include "mocFromH.h"
+    
+MyClass2::MyClass2(){}
+
+void MyClass2::myslot() {}
+
+void mocFromH() {
+  MyClass2 myclass;
+}
+
diff --git a/workspace/sconstools3/qt6/test/moc/auto/ccomment/image/mocFromH.h b/workspace/sconstools3/qt6/test/moc/auto/ccomment/image/mocFromH.h
new file mode 100644
index 0000000..6a96ca2
--- /dev/null
+++ b/workspace/sconstools3/qt6/test/moc/auto/ccomment/image/mocFromH.h
@@ -0,0 +1,32 @@
+#include <QObject>
+
+/*
+class MyClass2 : public QObject {
+  // Here we define a Q_OBJECT
+  Q_OBJECT;
+  public:
+  MyClass2();
+  public slots:
+  void myslot();
+};
+void mocFromH();
+
+*/
+
+// class MyClass2 : public QObject { Q_OBJECT; };
+
+class MyClass2
+{
+  // Q_OBJECT
+
+  /* and another Q_OBJECT but in
+   * a C comment,
+   * ... next Q_OBJECT
+   */
+
+public:
+  MyClass2();
+  void myslot();
+};
+
+void mocFromH();
diff --git a/workspace/sconstools3/qt6/test/moc/auto/ccomment/sconstest-ccomment.py b/workspace/sconstools3/qt6/test/moc/auto/ccomment/sconstest-ccomment.py
new file mode 100644
index 0000000..842909c
--- /dev/null
+++ b/workspace/sconstools3/qt6/test/moc/auto/ccomment/sconstest-ccomment.py
@@ -0,0 +1,46 @@
+#!/usr/bin/env python
+#
+# Copyright (c) 2001-2010,2011,2012 The SCons Foundation
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+"""
+A simple test for a few C/C++ comments, containing the
+string Q_OBJECT. With the QT5_GOBBLECOMMENTS variable set,
+no automocing should get triggered.
+"""
+
+import TestSCons
+
+test = TestSCons.TestSCons()
+test.dir_fixture('image')
+test.file_fixture('../../../qtenv.py')
+test.file_fixture('../../../../__init__.py','site_scons/site_tools/qt5/__init__.py')
+
+test.run()
+
+test.pass_test()
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/workspace/sconstools3/qt6/test/moc/auto/literalstring/image/SConscript b/workspace/sconstools3/qt6/test/moc/auto/literalstring/image/SConscript
new file mode 100644
index 0000000..1366183
--- /dev/null
+++ b/workspace/sconstools3/qt6/test/moc/auto/literalstring/image/SConscript
@@ -0,0 +1,6 @@
+Import("qtEnv")
+
+env = qtEnv.Clone()
+env.EnableQt5Modules(['QtCore','QtWidgets'])
+
+env.Program('main', Glob('*.cpp'))
diff --git a/workspace/sconstools3/qt6/test/moc/auto/literalstring/image/SConstruct b/workspace/sconstools3/qt6/test/moc/auto/literalstring/image/SConstruct
new file mode 100644
index 0000000..9754f10
--- /dev/null
+++ b/workspace/sconstools3/qt6/test/moc/auto/literalstring/image/SConstruct
@@ -0,0 +1,6 @@
+import qtenv
+
+qtEnv = qtenv.createQtEnvironment()
+Export('qtEnv')
+
+SConscript('SConscript')
diff --git a/workspace/sconstools3/qt6/test/moc/auto/literalstring/image/main.cpp b/workspace/sconstools3/qt6/test/moc/auto/literalstring/image/main.cpp
new file mode 100644
index 0000000..6d8b713
--- /dev/null
+++ b/workspace/sconstools3/qt6/test/moc/auto/literalstring/image/main.cpp
@@ -0,0 +1,14 @@
+#include "mocFromCpp.h"
+#include "mocFromH.h"
+
+#include <QtWidgets/QApplication>
+#include <stdio.h>
+    
+int main(int argc, char **argv)
+{
+  QApplication app(argc, argv);
+  mocFromCpp();
+  mocFromH();
+  printf("Hello World\n");
+  return 0;
+}
diff --git a/workspace/sconstools3/qt6/test/moc/auto/literalstring/image/mocFromCpp.cpp b/workspace/sconstools3/qt6/test/moc/auto/literalstring/image/mocFromCpp.cpp
new file mode 100644
index 0000000..407cc41
--- /dev/null
+++ b/workspace/sconstools3/qt6/test/moc/auto/literalstring/image/mocFromCpp.cpp
@@ -0,0 +1,27 @@
+#include "mocFromCpp.h"
+
+#include <QObject>
+#include <stdio.h>
+
+class MyClass1
+{
+public:
+  MyClass1() {};
+  void myslot() {};
+  void printme()
+  {
+    printf("It's a Q_OBJECT!");
+    printf("Concatenating "
+           "a Q_OBJECT"
+           " with another "
+           "Q_OBJECT"
+           "and a "
+           "Q_OBJECT, finally");
+  };
+
+};
+
+void mocFromCpp()
+{
+  MyClass1 myclass;
+}
diff --git a/workspace/sconstools3/qt6/test/moc/auto/literalstring/image/mocFromCpp.h b/workspace/sconstools3/qt6/test/moc/auto/literalstring/image/mocFromCpp.h
new file mode 100644
index 0000000..fc1e04c
--- /dev/null
+++ b/workspace/sconstools3/qt6/test/moc/auto/literalstring/image/mocFromCpp.h
@@ -0,0 +1,2 @@
+void mocFromCpp();
+
diff --git a/workspace/sconstools3/qt6/test/moc/auto/literalstring/image/mocFromH.cpp b/workspace/sconstools3/qt6/test/moc/auto/literalstring/image/mocFromH.cpp
new file mode 100644
index 0000000..7cfb2be
--- /dev/null
+++ b/workspace/sconstools3/qt6/test/moc/auto/literalstring/image/mocFromH.cpp
@@ -0,0 +1,10 @@
+#include "mocFromH.h"
+    
+MyClass2::MyClass2(){}
+
+void MyClass2::myslot() {}
+
+void mocFromH() {
+  MyClass2 myclass;
+}
+
diff --git a/workspace/sconstools3/qt6/test/moc/auto/literalstring/image/mocFromH.h b/workspace/sconstools3/qt6/test/moc/auto/literalstring/image/mocFromH.h
new file mode 100644
index 0000000..6868c89
--- /dev/null
+++ b/workspace/sconstools3/qt6/test/moc/auto/literalstring/image/mocFromH.h
@@ -0,0 +1,21 @@
+#include <QObject>
+#include <stdio.h>
+
+class MyClass2
+{
+public:
+  MyClass2();
+  void myslot();
+  void printme()
+  {
+    printf("It's a Q_OBJECT!");
+    printf("Concatenating "
+           "a Q_OBJECT"
+           " with another "
+           "Q_OBJECT"
+           "and a "
+           "Q_OBJECT, finally");
+  };
+};
+
+void mocFromH();
diff --git a/workspace/sconstools3/qt6/test/moc/auto/literalstring/sconstest-literalstring.py b/workspace/sconstools3/qt6/test/moc/auto/literalstring/sconstest-literalstring.py
new file mode 100644
index 0000000..3d63113
--- /dev/null
+++ b/workspace/sconstools3/qt6/test/moc/auto/literalstring/sconstest-literalstring.py
@@ -0,0 +1,45 @@
+#!/usr/bin/env python
+#
+# Copyright (c) 2001-2010,2011,2012 The SCons Foundation
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+"""
+A simple test, ensuring that the Q_OBJECT keyword in a 
+literal C/C++ string does not trigger automocing.
+"""
+
+import TestSCons
+
+test = TestSCons.TestSCons()
+test.dir_fixture('image')
+test.file_fixture('../../../qtenv.py')
+test.file_fixture('../../../../__init__.py','site_scons/site_tools/qt5/__init__.py')
+
+test.run()
+
+test.pass_test()
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/workspace/sconstools3/qt6/test/moc/cpppath/default/SConscript b/workspace/sconstools3/qt6/test/moc/cpppath/default/SConscript
new file mode 100644
index 0000000..81a9851
--- /dev/null
+++ b/workspace/sconstools3/qt6/test/moc/cpppath/default/SConscript
@@ -0,0 +1,8 @@
+Import("qtEnv")
+
+env = qtEnv.Clone()
+env.EnableQt5Modules(['QtCore','QtWidgets'])
+env.Append(CPPPATH=['include'])
+
+env.Program('main', Glob('src/*.cpp'))
+
diff --git a/workspace/sconstools3/qt6/test/moc/cpppath/default/SConscript-fails b/workspace/sconstools3/qt6/test/moc/cpppath/default/SConscript-fails
new file mode 100644
index 0000000..8e3fcc6
--- /dev/null
+++ b/workspace/sconstools3/qt6/test/moc/cpppath/default/SConscript-fails
@@ -0,0 +1,9 @@
+Import("qtEnv")
+
+env = qtEnv.Clone()
+env.EnableQt5Modules(['QtCore','QtWidgets'])
+env.Append(CPPPATH=['include'])
+env['QT5_AUTOMOC_SCANCPPPATH']='0'
+
+env.Program('main', Glob('src/*.cpp'))
+
diff --git a/workspace/sconstools3/qt6/test/moc/cpppath/default/image/SConstruct b/workspace/sconstools3/qt6/test/moc/cpppath/default/image/SConstruct
new file mode 100644
index 0000000..9754f10
--- /dev/null
+++ b/workspace/sconstools3/qt6/test/moc/cpppath/default/image/SConstruct
@@ -0,0 +1,6 @@
+import qtenv
+
+qtEnv = qtenv.createQtEnvironment()
+Export('qtEnv')
+
+SConscript('SConscript')
diff --git a/workspace/sconstools3/qt6/test/moc/cpppath/default/image/src/main.cpp b/workspace/sconstools3/qt6/test/moc/cpppath/default/image/src/main.cpp
new file mode 100644
index 0000000..6d8b713
--- /dev/null
+++ b/workspace/sconstools3/qt6/test/moc/cpppath/default/image/src/main.cpp
@@ -0,0 +1,14 @@
+#include "mocFromCpp.h"
+#include "mocFromH.h"
+
+#include <QtWidgets/QApplication>
+#include <stdio.h>
+    
+int main(int argc, char **argv)
+{
+  QApplication app(argc, argv);
+  mocFromCpp();
+  mocFromH();
+  printf("Hello World\n");
+  return 0;
+}
diff --git a/workspace/sconstools3/qt6/test/moc/cpppath/default/image/src/mocFromCpp.cpp b/workspace/sconstools3/qt6/test/moc/cpppath/default/image/src/mocFromCpp.cpp
new file mode 100644
index 0000000..e18d1b8
--- /dev/null
+++ b/workspace/sconstools3/qt6/test/moc/cpppath/default/image/src/mocFromCpp.cpp
@@ -0,0 +1,15 @@
+#include <QObject>
+
+#include "mocFromCpp.h"
+
+class MyClass1 : public QObject {
+  Q_OBJECT
+  public:
+  MyClass1() : QObject() {};
+  public slots:
+  void myslot() {};
+};
+void mocFromCpp() {
+  MyClass1 myclass;
+}
+#include "mocFromCpp.moc"
diff --git a/workspace/sconstools3/qt6/test/moc/cpppath/default/image/src/mocFromH.cpp b/workspace/sconstools3/qt6/test/moc/cpppath/default/image/src/mocFromH.cpp
new file mode 100644
index 0000000..7cfb2be
--- /dev/null
+++ b/workspace/sconstools3/qt6/test/moc/cpppath/default/image/src/mocFromH.cpp
@@ -0,0 +1,10 @@
+#include "mocFromH.h"
+    
+MyClass2::MyClass2(){}
+
+void MyClass2::myslot() {}
+
+void mocFromH() {
+  MyClass2 myclass;
+}
+
diff --git a/workspace/sconstools3/qt6/test/moc/cpppath/default/sconstest-default-fails.py b/workspace/sconstools3/qt6/test/moc/cpppath/default/sconstest-default-fails.py
new file mode 100644
index 0000000..469c0b5
--- /dev/null
+++ b/workspace/sconstools3/qt6/test/moc/cpppath/default/sconstest-default-fails.py
@@ -0,0 +1,46 @@
+#!/usr/bin/env python
+#
+# Copyright (c) 2001-2010,2011,2012 The SCons Foundation
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+"""
+Disabled the CPPPATH support of the Automoc feature:
+uses the CPPPATH list of the current environment, but still fails.
+"""
+
+import TestSCons
+
+test = TestSCons.TestSCons()
+test.dir_fixture('image')
+test.file_fixture('SConscript-fails','SConscript')
+test.file_fixture('../../../qtenv.py')
+test.file_fixture('../../../../__init__.py','site_scons/site_tools/qt5/__init__.py')
+
+test.run(status=2, stderr=None)
+
+test.pass_test()
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/workspace/sconstools3/qt6/test/moc/cpppath/default/sconstest-default.py b/workspace/sconstools3/qt6/test/moc/cpppath/default/sconstest-default.py
new file mode 100644
index 0000000..586deb3
--- /dev/null
+++ b/workspace/sconstools3/qt6/test/moc/cpppath/default/sconstest-default.py
@@ -0,0 +1,46 @@
+#!/usr/bin/env python
+#
+# Copyright (c) 2001-2010,2011,2012 The SCons Foundation
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+"""
+Default settings for the CPPPATH support of the Automoc feature:
+it is enabled and uses the CPPPATH list of the current environment.
+"""
+
+import TestSCons
+
+test = TestSCons.TestSCons()
+test.dir_fixture('image')
+test.file_fixture('SConscript','SConscript')
+test.file_fixture('../../../qtenv.py')
+test.file_fixture('../../../../__init__.py','site_scons/site_tools/qt5/__init__.py')
+
+test.run()
+
+test.pass_test()
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/workspace/sconstools3/qt6/test/moc/cpppath/specific/SConscript b/workspace/sconstools3/qt6/test/moc/cpppath/specific/SConscript
new file mode 100644
index 0000000..ea6157e
--- /dev/null
+++ b/workspace/sconstools3/qt6/test/moc/cpppath/specific/SConscript
@@ -0,0 +1,9 @@
+Import("qtEnv")
+
+env = qtEnv.Clone()
+env.EnableQt5Modules(['QtCore','QtWidgets'])
+env.Append(CPPPATH=['include'])
+env['QT5_AUTOMOC_CPPPATH']=['include']
+
+env.Program('main', Glob('src/*.cpp'))
+
diff --git a/workspace/sconstools3/qt6/test/moc/cpppath/specific/SConscript-fails b/workspace/sconstools3/qt6/test/moc/cpppath/specific/SConscript-fails
new file mode 100644
index 0000000..90689ee
--- /dev/null
+++ b/workspace/sconstools3/qt6/test/moc/cpppath/specific/SConscript-fails
@@ -0,0 +1,9 @@
+Import("qtEnv")
+
+env = qtEnv.Clone()
+env.EnableQt5Modules(['QtCore','QtWidgets'])
+env.Append(CPPPATH=['include'])
+env['QT5_AUTOMOC_CPPPATH']=['wrongdir']
+
+env.Program('main', Glob('src/*.cpp'))
+
diff --git a/workspace/sconstools3/qt6/test/moc/cpppath/specific/image/SConstruct b/workspace/sconstools3/qt6/test/moc/cpppath/specific/image/SConstruct
new file mode 100644
index 0000000..9754f10
--- /dev/null
+++ b/workspace/sconstools3/qt6/test/moc/cpppath/specific/image/SConstruct
@@ -0,0 +1,6 @@
+import qtenv
+
+qtEnv = qtenv.createQtEnvironment()
+Export('qtEnv')
+
+SConscript('SConscript')
diff --git a/workspace/sconstools3/qt6/test/moc/cpppath/specific/image/src/main.cpp b/workspace/sconstools3/qt6/test/moc/cpppath/specific/image/src/main.cpp
new file mode 100644
index 0000000..6d8b713
--- /dev/null
+++ b/workspace/sconstools3/qt6/test/moc/cpppath/specific/image/src/main.cpp
@@ -0,0 +1,14 @@
+#include "mocFromCpp.h"
+#include "mocFromH.h"
+
+#include <QtWidgets/QApplication>
+#include <stdio.h>
+    
+int main(int argc, char **argv)
+{
+  QApplication app(argc, argv);
+  mocFromCpp();
+  mocFromH();
+  printf("Hello World\n");
+  return 0;
+}
diff --git a/workspace/sconstools3/qt6/test/moc/cpppath/specific/image/src/mocFromCpp.cpp b/workspace/sconstools3/qt6/test/moc/cpppath/specific/image/src/mocFromCpp.cpp
new file mode 100644
index 0000000..e18d1b8
--- /dev/null
+++ b/workspace/sconstools3/qt6/test/moc/cpppath/specific/image/src/mocFromCpp.cpp
@@ -0,0 +1,15 @@
+#include <QObject>
+
+#include "mocFromCpp.h"
+
+class MyClass1 : public QObject {
+  Q_OBJECT
+  public:
+  MyClass1() : QObject() {};
+  public slots:
+  void myslot() {};
+};
+void mocFromCpp() {
+  MyClass1 myclass;
+}
+#include "mocFromCpp.moc"
diff --git a/workspace/sconstools3/qt6/test/moc/cpppath/specific/image/src/mocFromH.cpp b/workspace/sconstools3/qt6/test/moc/cpppath/specific/image/src/mocFromH.cpp
new file mode 100644
index 0000000..7cfb2be
--- /dev/null
+++ b/workspace/sconstools3/qt6/test/moc/cpppath/specific/image/src/mocFromH.cpp
@@ -0,0 +1,10 @@
+#include "mocFromH.h"
+    
+MyClass2::MyClass2(){}
+
+void MyClass2::myslot() {}
+
+void mocFromH() {
+  MyClass2 myclass;
+}
+
diff --git a/workspace/sconstools3/qt6/test/moc/cpppath/specific/sconstest-specific-fails.py b/workspace/sconstools3/qt6/test/moc/cpppath/specific/sconstest-specific-fails.py
new file mode 100644
index 0000000..b301e80
--- /dev/null
+++ b/workspace/sconstools3/qt6/test/moc/cpppath/specific/sconstest-specific-fails.py
@@ -0,0 +1,49 @@
+#!/usr/bin/env python
+#
+# Copyright (c) 2001-2010,2011,2012 The SCons Foundation
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+"""
+Explicitly sets the path list to scan for mocable files,
+but to a wrong folder.
+Although the correct CPPPATH is used, this lets the test
+fail...and proves that the option QT5_AUTOMOC_CPPPATH
+really has an effect.
+"""
+
+import TestSCons
+
+test = TestSCons.TestSCons()
+test.dir_fixture('image')
+test.file_fixture('SConscript-fails','SConscript')
+test.file_fixture('../../../qtenv.py')
+test.file_fixture('../../../../__init__.py','site_scons/site_tools/qt5/__init__.py')
+
+test.run(status=2, stderr=None)
+
+test.pass_test()
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/workspace/sconstools3/qt6/test/moc/cpppath/specific/sconstest-specific.py b/workspace/sconstools3/qt6/test/moc/cpppath/specific/sconstest-specific.py
new file mode 100644
index 0000000..a9ba087
--- /dev/null
+++ b/workspace/sconstools3/qt6/test/moc/cpppath/specific/sconstest-specific.py
@@ -0,0 +1,48 @@
+#!/usr/bin/env python
+#
+# Copyright (c) 2001-2010,2011,2012 The SCons Foundation
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+"""
+Explicitly sets the path list to scan for moc'able files.
+The correct CPPPATH is used too, but gets only searched for
+normal implicit header dependencies (as is proved by the
+the accompanying test that fails).
+"""
+
+import TestSCons
+
+test = TestSCons.TestSCons()
+test.dir_fixture('image')
+test.file_fixture('SConscript','SConscript')
+test.file_fixture('../../../qtenv.py')
+test.file_fixture('../../../../__init__.py','site_scons/site_tools/qt5/__init__.py')
+
+test.run()
+
+test.pass_test()
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/workspace/sconstools3/qt6/test/moc/explicit/image/SConscript b/workspace/sconstools3/qt6/test/moc/explicit/image/SConscript
new file mode 100644
index 0000000..f224ec9
--- /dev/null
+++ b/workspace/sconstools3/qt6/test/moc/explicit/image/SConscript
@@ -0,0 +1,10 @@
+Import("qtEnv")
+
+env = qtEnv.Clone()
+env.EnableQt5Modules(['QtCore','QtWidgets'])
+env['QT5_AUTOSCAN']=0
+
+env.ExplicitMoc5('explicitly_moced_FromHeader.cpp','mocFromH.h')
+env.ExplicitMoc5('explicitly_moced_FromCpp.strange_cpp_moc_prefix','mocFromCpp.cpp')
+
+env.Program('main', Glob('*.cpp'))
diff --git a/workspace/sconstools3/qt6/test/moc/explicit/image/SConstruct b/workspace/sconstools3/qt6/test/moc/explicit/image/SConstruct
new file mode 100644
index 0000000..9754f10
--- /dev/null
+++ b/workspace/sconstools3/qt6/test/moc/explicit/image/SConstruct
@@ -0,0 +1,6 @@
+import qtenv
+
+qtEnv = qtenv.createQtEnvironment()
+Export('qtEnv')
+
+SConscript('SConscript')
diff --git a/workspace/sconstools3/qt6/test/moc/explicit/image/main.cpp b/workspace/sconstools3/qt6/test/moc/explicit/image/main.cpp
new file mode 100644
index 0000000..6d8b713
--- /dev/null
+++ b/workspace/sconstools3/qt6/test/moc/explicit/image/main.cpp
@@ -0,0 +1,14 @@
+#include "mocFromCpp.h"
+#include "mocFromH.h"
+
+#include <QtWidgets/QApplication>
+#include <stdio.h>
+    
+int main(int argc, char **argv)
+{
+  QApplication app(argc, argv);
+  mocFromCpp();
+  mocFromH();
+  printf("Hello World\n");
+  return 0;
+}
diff --git a/workspace/sconstools3/qt6/test/moc/explicit/image/mocFromCpp.cpp b/workspace/sconstools3/qt6/test/moc/explicit/image/mocFromCpp.cpp
new file mode 100644
index 0000000..669d7a1
--- /dev/null
+++ b/workspace/sconstools3/qt6/test/moc/explicit/image/mocFromCpp.cpp
@@ -0,0 +1,16 @@
+#include <QObject>
+
+#include "mocFromCpp.h"
+
+class MyClass1 : public QObject {
+  Q_OBJECT
+  public:
+  MyClass1() : QObject() {};
+  public slots:
+  void myslot() {};
+};
+void mocFromCpp() {
+  MyClass1 myclass;
+}
+#include "explicitly_moced_FromCpp.strange_cpp_moc_prefix"
+
diff --git a/workspace/sconstools3/qt6/test/moc/explicit/image/mocFromCpp.h b/workspace/sconstools3/qt6/test/moc/explicit/image/mocFromCpp.h
new file mode 100644
index 0000000..fc1e04c
--- /dev/null
+++ b/workspace/sconstools3/qt6/test/moc/explicit/image/mocFromCpp.h
@@ -0,0 +1,2 @@
+void mocFromCpp();
+
diff --git a/workspace/sconstools3/qt6/test/moc/explicit/image/mocFromH.cpp b/workspace/sconstools3/qt6/test/moc/explicit/image/mocFromH.cpp
new file mode 100644
index 0000000..586aafb
--- /dev/null
+++ b/workspace/sconstools3/qt6/test/moc/explicit/image/mocFromH.cpp
@@ -0,0 +1,8 @@
+#include "mocFromH.h"
+    
+MyClass2::MyClass2() : QObject() {}
+void MyClass2::myslot() {}
+void mocFromH() {
+  MyClass2 myclass;
+}
+
diff --git a/workspace/sconstools3/qt6/test/moc/explicit/image/mocFromH.h b/workspace/sconstools3/qt6/test/moc/explicit/image/mocFromH.h
new file mode 100644
index 0000000..48fdd7e
--- /dev/null
+++ b/workspace/sconstools3/qt6/test/moc/explicit/image/mocFromH.h
@@ -0,0 +1,11 @@
+#include <QObject>
+
+class MyClass2 : public QObject {
+  Q_OBJECT;
+  public:
+  MyClass2();
+  public slots:
+  void myslot();
+};
+void mocFromH();
+
diff --git a/workspace/sconstools3/qt6/test/moc/explicit/sconstest-explicit.py b/workspace/sconstools3/qt6/test/moc/explicit/sconstest-explicit.py
new file mode 100644
index 0000000..f5e78fe
--- /dev/null
+++ b/workspace/sconstools3/qt6/test/moc/explicit/sconstest-explicit.py
@@ -0,0 +1,45 @@
+#!/usr/bin/env python
+#
+# Copyright (c) 2001-2010,2011,2012 The SCons Foundation
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+"""
+Test of the explicit Moc5 builder, with arbitrary filenames
+for the created files.
+"""
+
+import TestSCons
+
+test = TestSCons.TestSCons()
+test.dir_fixture('image')
+test.file_fixture('../../qtenv.py')
+test.file_fixture('../../../__init__.py','site_scons/site_tools/qt5/__init__.py')
+
+test.run()
+
+test.pass_test()
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/workspace/sconstools3/qt6/test/moc/order_independent/image/SConscript b/workspace/sconstools3/qt6/test/moc/order_independent/image/SConscript
new file mode 100644
index 0000000..a8b791c
--- /dev/null
+++ b/workspace/sconstools3/qt6/test/moc/order_independent/image/SConscript
@@ -0,0 +1,12 @@
+Import("qtEnv dup reverted")
+
+qtEnv.EnableQt5Modules(['QtCore','QtGui'])
+
+if dup == 0: 
+  qtEnv.Append(CPPPATH=['.'])
+
+if reverted == 0:
+  qtEnv.Program('aaa',['aaa.cpp','bbb.cpp'])
+else:
+  qtEnv.Program('aaa',['bbb.cpp','aaa.cpp'])
+
diff --git a/workspace/sconstools3/qt6/test/moc/order_independent/image/SConstruct b/workspace/sconstools3/qt6/test/moc/order_independent/image/SConstruct
new file mode 100644
index 0000000..5490ee2
--- /dev/null
+++ b/workspace/sconstools3/qt6/test/moc/order_independent/image/SConstruct
@@ -0,0 +1,26 @@
+from __future__ import print_function
+import qtenv
+
+qtEnv = qtenv.createQtEnvironment()
+
+reverted=int(ARGUMENTS.get('reverted', 0))
+dup = 1
+if ARGUMENTS.get('variant_dir', 0):
+    if ARGUMENTS.get('chdir', 0):
+        SConscriptChdir(1)
+    else:
+        SConscriptChdir(0)
+    dup=int(ARGUMENTS.get('dup', 1))
+    if dup == 0:
+        builddir = 'build_dup0'
+        qtEnv['QT5_DEBUG'] = 1
+    else:
+        builddir = 'build'
+    VariantDir(builddir, '.', duplicate=dup)
+    print(builddir, dup)
+    sconscript = Dir(builddir).File('SConscript')
+else:
+    sconscript = File('SConscript')
+
+Export("qtEnv dup reverted")
+SConscript( sconscript )
diff --git a/workspace/sconstools3/qt6/test/moc/order_independent/image/aaa.cpp b/workspace/sconstools3/qt6/test/moc/order_independent/image/aaa.cpp
new file mode 100644
index 0000000..054d582
--- /dev/null
+++ b/workspace/sconstools3/qt6/test/moc/order_independent/image/aaa.cpp
@@ -0,0 +1,7 @@
+#include "aaa.h"
+
+int main() 
+{ 
+  aaa a; 
+  return 0; 
+}
diff --git a/workspace/sconstools3/qt6/test/moc/order_independent/image/aaa.h b/workspace/sconstools3/qt6/test/moc/order_independent/image/aaa.h
new file mode 100644
index 0000000..3438edd
--- /dev/null
+++ b/workspace/sconstools3/qt6/test/moc/order_independent/image/aaa.h
@@ -0,0 +1,9 @@
+#include <QObject>
+
+class aaa : public QObject
+{
+  Q_OBJECT
+
+public:
+  aaa() {};
+};
diff --git a/workspace/sconstools3/qt6/test/moc/order_independent/image/bbb.cpp b/workspace/sconstools3/qt6/test/moc/order_independent/image/bbb.cpp
new file mode 100644
index 0000000..6fc20da
--- /dev/null
+++ b/workspace/sconstools3/qt6/test/moc/order_independent/image/bbb.cpp
@@ -0,0 +1,4 @@
+int bbb() 
+{ 
+  return 0; 
+}
diff --git a/workspace/sconstools3/qt6/test/moc/order_independent/sconstest-order-independent.py b/workspace/sconstools3/qt6/test/moc/order_independent/sconstest-order-independent.py
new file mode 100644
index 0000000..083bd7b
--- /dev/null
+++ b/workspace/sconstools3/qt6/test/moc/order_independent/sconstest-order-independent.py
@@ -0,0 +1,46 @@
+#!/usr/bin/env python
+#
+# Copyright (c) 2001-2010,2011,2012 The SCons Foundation
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+"""
+Ensures that no rebuilds get triggered when the order of the source
+list changes in the Automoc routine(s).
+"""
+
+import TestSCons
+
+test = TestSCons.TestSCons()
+test.dir_fixture('image')
+test.file_fixture('../../qtenv.py')
+test.file_fixture('../../../__init__.py','site_scons/site_tools/qt5/__init__.py')
+
+test.run()
+test.up_to_date(options="-n", arguments=".")
+test.up_to_date(options="-n reverted=1", arguments=".")
+test.pass_test()
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/workspace/sconstools3/qt6/test/qrc/basic/SConscript b/workspace/sconstools3/qt6/test/qrc/basic/SConscript
new file mode 100644
index 0000000..1bcb156
--- /dev/null
+++ b/workspace/sconstools3/qt6/test/qrc/basic/SConscript
@@ -0,0 +1,8 @@
+Import('qtEnv')
+
+env = qtEnv.Clone()
+env.EnableQt5Modules(['QtCore','QtWidgets'])
+
+source_files = Glob('*.cpp')+Glob('*.qrc')
+
+env.Program('main', source_files)
diff --git a/workspace/sconstools3/qt6/test/qrc/basic/SConscript-wflags b/workspace/sconstools3/qt6/test/qrc/basic/SConscript-wflags
new file mode 100644
index 0000000..bf987fc
--- /dev/null
+++ b/workspace/sconstools3/qt6/test/qrc/basic/SConscript-wflags
@@ -0,0 +1,9 @@
+Import('qtEnv')
+
+env = qtEnv.Clone()
+env.EnableQt5Modules(['QtCore','QtWidgets'])
+
+source_files = Glob('*.cpp')+Glob('*.qrc')
+env['QT5_QRCFLAGS'] = ['-name', 'icons']
+
+env.Program('main', source_files)
diff --git a/workspace/sconstools3/qt6/test/qrc/basic/image/SConstruct b/workspace/sconstools3/qt6/test/qrc/basic/image/SConstruct
new file mode 100644
index 0000000..00e5705
--- /dev/null
+++ b/workspace/sconstools3/qt6/test/qrc/basic/image/SConstruct
@@ -0,0 +1,6 @@
+import qtenv
+
+qtEnv = qtenv.createQtEnvironment()
+Export('qtEnv')
+SConscript('SConscript')
+    
\ No newline at end of file
diff --git a/workspace/sconstools3/qt6/test/qrc/basic/image/icons.qrc b/workspace/sconstools3/qt6/test/qrc/basic/image/icons.qrc
new file mode 100644
index 0000000..86fe71b
--- /dev/null
+++ b/workspace/sconstools3/qt6/test/qrc/basic/image/icons.qrc
@@ -0,0 +1,5 @@
+<!DOCTYPE RCC><RCC version="1.0">
+<qresource prefix="/">
+    <file>icons/scons.png</file>
+</qresource>
+</RCC>
diff --git a/workspace/sconstools3/qt6/test/qrc/basic/image/icons/scons.png b/workspace/sconstools3/qt6/test/qrc/basic/image/icons/scons.png
new file mode 100644
index 0000000..7d85f1f
Binary files /dev/null and b/workspace/sconstools3/qt6/test/qrc/basic/image/icons/scons.png differ
diff --git a/workspace/sconstools3/qt6/test/qrc/basic/image/main.cpp b/workspace/sconstools3/qt6/test/qrc/basic/image/main.cpp
new file mode 100644
index 0000000..304dc3a
--- /dev/null
+++ b/workspace/sconstools3/qt6/test/qrc/basic/image/main.cpp
@@ -0,0 +1,11 @@
+#include <QtWidgets/QApplication>
+#include <QtCore>
+
+int main(int argc, char *argv[])
+{
+    QApplication app(argc, argv);
+
+    Q_INIT_RESOURCE(icons);
+
+    return 0;
+}
diff --git a/workspace/sconstools3/qt6/test/qrc/basic/sconstest-basic.py b/workspace/sconstools3/qt6/test/qrc/basic/sconstest-basic.py
new file mode 100644
index 0000000..e88cc9e
--- /dev/null
+++ b/workspace/sconstools3/qt6/test/qrc/basic/sconstest-basic.py
@@ -0,0 +1,45 @@
+#!/usr/bin/env python
+#
+# Copyright (c) 2001-2010,2011,2012 The SCons Foundation
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+
+"""
+Basic test for the Qrc() builder.
+"""
+
+import TestSCons
+
+test = TestSCons.TestSCons()
+test.dir_fixture("image")
+test.file_fixture('SConscript')
+test.file_fixture('../../qtenv.py')
+test.file_fixture('../../../__init__.py','site_scons/site_tools/qt5/__init__.py')
+test.run()
+
+test.pass_test()
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/workspace/sconstools3/qt6/test/qrc/basic/sconstest-basicwflags.py b/workspace/sconstools3/qt6/test/qrc/basic/sconstest-basicwflags.py
new file mode 100644
index 0000000..4d1b92d
--- /dev/null
+++ b/workspace/sconstools3/qt6/test/qrc/basic/sconstest-basicwflags.py
@@ -0,0 +1,45 @@
+#!/usr/bin/env python
+#
+# Copyright (c) 2001-2010,2011,2012 The SCons Foundation
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+
+"""
+Basic test for the Qrc() builder, with '-name' flag set.
+"""
+
+import TestSCons
+
+test = TestSCons.TestSCons()
+test.dir_fixture("image")
+test.file_fixture('SConscript-wflags','SConscript')
+test.file_fixture('../../qtenv.py')
+test.file_fixture('../../../__init__.py','site_scons/site_tools/qt5/__init__.py')
+test.run()
+
+test.pass_test()
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/workspace/sconstools3/qt6/test/qrc/manual/SConscript b/workspace/sconstools3/qt6/test/qrc/manual/SConscript
new file mode 100644
index 0000000..8445a06
--- /dev/null
+++ b/workspace/sconstools3/qt6/test/qrc/manual/SConscript
@@ -0,0 +1,9 @@
+Import('qtEnv')
+
+env = qtEnv.Clone()
+env.EnableQt5Modules(['QtCore','QtWidgets'])
+
+source_files = Glob('*.cpp')
+source_files.append(env.Qrc5('icons'))
+
+env.Program('main', source_files)
diff --git a/workspace/sconstools3/qt6/test/qrc/manual/SConscript-wflags b/workspace/sconstools3/qt6/test/qrc/manual/SConscript-wflags
new file mode 100644
index 0000000..961c00b
--- /dev/null
+++ b/workspace/sconstools3/qt6/test/qrc/manual/SConscript-wflags
@@ -0,0 +1,11 @@
+Import('qtEnv')
+
+env = qtEnv.Clone()
+env.EnableQt5Modules(['QtCore','QtWidgets'])
+
+source_files = Glob('*.cpp')
+source_files.append(env.Qrc5('icons'))
+
+env['QT5_QRCFLAGS'] = ['-name', 'icons']
+
+env.Program('main', source_files)
diff --git a/workspace/sconstools3/qt6/test/qrc/manual/image/SConstruct b/workspace/sconstools3/qt6/test/qrc/manual/image/SConstruct
new file mode 100644
index 0000000..00e5705
--- /dev/null
+++ b/workspace/sconstools3/qt6/test/qrc/manual/image/SConstruct
@@ -0,0 +1,6 @@
+import qtenv
+
+qtEnv = qtenv.createQtEnvironment()
+Export('qtEnv')
+SConscript('SConscript')
+    
\ No newline at end of file
diff --git a/workspace/sconstools3/qt6/test/qrc/manual/image/icons.qrc b/workspace/sconstools3/qt6/test/qrc/manual/image/icons.qrc
new file mode 100644
index 0000000..86fe71b
--- /dev/null
+++ b/workspace/sconstools3/qt6/test/qrc/manual/image/icons.qrc
@@ -0,0 +1,5 @@
+<!DOCTYPE RCC><RCC version="1.0">
+<qresource prefix="/">
+    <file>icons/scons.png</file>
+</qresource>
+</RCC>
diff --git a/workspace/sconstools3/qt6/test/qrc/manual/image/icons/scons.png b/workspace/sconstools3/qt6/test/qrc/manual/image/icons/scons.png
new file mode 100644
index 0000000..7d85f1f
Binary files /dev/null and b/workspace/sconstools3/qt6/test/qrc/manual/image/icons/scons.png differ
diff --git a/workspace/sconstools3/qt6/test/qrc/manual/image/main.cpp b/workspace/sconstools3/qt6/test/qrc/manual/image/main.cpp
new file mode 100644
index 0000000..304dc3a
--- /dev/null
+++ b/workspace/sconstools3/qt6/test/qrc/manual/image/main.cpp
@@ -0,0 +1,11 @@
+#include <QtWidgets/QApplication>
+#include <QtCore>
+
+int main(int argc, char *argv[])
+{
+    QApplication app(argc, argv);
+
+    Q_INIT_RESOURCE(icons);
+
+    return 0;
+}
diff --git a/workspace/sconstools3/qt6/test/qrc/manual/sconstest-manual.py b/workspace/sconstools3/qt6/test/qrc/manual/sconstest-manual.py
new file mode 100644
index 0000000..0a8fac9
--- /dev/null
+++ b/workspace/sconstools3/qt6/test/qrc/manual/sconstest-manual.py
@@ -0,0 +1,45 @@
+#!/usr/bin/env python
+#
+# Copyright (c) 2001-2010,2011,2012 The SCons Foundation
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+
+"""
+Basic test for the Qrc() builder, called explicitly.
+"""
+
+import TestSCons
+
+test = TestSCons.TestSCons()
+test.dir_fixture("image")
+test.file_fixture('SConscript')
+test.file_fixture('../../qtenv.py')
+test.file_fixture('../../../__init__.py','site_scons/site_tools/qt5/__init__.py')
+test.run()
+
+test.pass_test()
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/workspace/sconstools3/qt6/test/qrc/manual/sconstest-manualwflags.py b/workspace/sconstools3/qt6/test/qrc/manual/sconstest-manualwflags.py
new file mode 100644
index 0000000..aef66c7
--- /dev/null
+++ b/workspace/sconstools3/qt6/test/qrc/manual/sconstest-manualwflags.py
@@ -0,0 +1,45 @@
+#!/usr/bin/env python
+#
+# Copyright (c) 2001-2010,2011,2012 The SCons Foundation
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+
+"""
+Basic test for the Qrc() builder, called explicitly with '-name' flag set.
+"""
+
+import TestSCons
+
+test = TestSCons.TestSCons()
+test.dir_fixture("image")
+test.file_fixture('SConscript-wflags','SConscript')
+test.file_fixture('../../qtenv.py')
+test.file_fixture('../../../__init__.py','site_scons/site_tools/qt5/__init__.py')
+test.run()
+
+test.pass_test()
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/workspace/sconstools3/qt6/test/qrc/multifiles/SConscript b/workspace/sconstools3/qt6/test/qrc/multifiles/SConscript
new file mode 100644
index 0000000..1bcb156
--- /dev/null
+++ b/workspace/sconstools3/qt6/test/qrc/multifiles/SConscript
@@ -0,0 +1,8 @@
+Import('qtEnv')
+
+env = qtEnv.Clone()
+env.EnableQt5Modules(['QtCore','QtWidgets'])
+
+source_files = Glob('*.cpp')+Glob('*.qrc')
+
+env.Program('main', source_files)
diff --git a/workspace/sconstools3/qt6/test/qrc/multifiles/SConscript-manual b/workspace/sconstools3/qt6/test/qrc/multifiles/SConscript-manual
new file mode 100644
index 0000000..d77c2f8
--- /dev/null
+++ b/workspace/sconstools3/qt6/test/qrc/multifiles/SConscript-manual
@@ -0,0 +1,9 @@
+Import('qtEnv')
+
+env = qtEnv.Clone()
+env.EnableQt5Modules(['QtCore','QtWidgets'])
+
+source_files = Glob('*.cpp')
+qrc_files = env.Qrc5(['icons','other'])
+
+env.Program('main', source_files+qrc_files)
diff --git a/workspace/sconstools3/qt6/test/qrc/multifiles/image/SConstruct b/workspace/sconstools3/qt6/test/qrc/multifiles/image/SConstruct
new file mode 100644
index 0000000..00e5705
--- /dev/null
+++ b/workspace/sconstools3/qt6/test/qrc/multifiles/image/SConstruct
@@ -0,0 +1,6 @@
+import qtenv
+
+qtEnv = qtenv.createQtEnvironment()
+Export('qtEnv')
+SConscript('SConscript')
+    
\ No newline at end of file
diff --git a/workspace/sconstools3/qt6/test/qrc/multifiles/image/icons.qrc b/workspace/sconstools3/qt6/test/qrc/multifiles/image/icons.qrc
new file mode 100644
index 0000000..86fe71b
--- /dev/null
+++ b/workspace/sconstools3/qt6/test/qrc/multifiles/image/icons.qrc
@@ -0,0 +1,5 @@
+<!DOCTYPE RCC><RCC version="1.0">
+<qresource prefix="/">
+    <file>icons/scons.png</file>
+</qresource>
+</RCC>
diff --git a/workspace/sconstools3/qt6/test/qrc/multifiles/image/icons/scons.png b/workspace/sconstools3/qt6/test/qrc/multifiles/image/icons/scons.png
new file mode 100644
index 0000000..7d85f1f
Binary files /dev/null and b/workspace/sconstools3/qt6/test/qrc/multifiles/image/icons/scons.png differ
diff --git a/workspace/sconstools3/qt6/test/qrc/multifiles/image/main.cpp b/workspace/sconstools3/qt6/test/qrc/multifiles/image/main.cpp
new file mode 100644
index 0000000..325478d
--- /dev/null
+++ b/workspace/sconstools3/qt6/test/qrc/multifiles/image/main.cpp
@@ -0,0 +1,12 @@
+#include <QtWidgets/QApplication>
+#include <QtCore>
+
+int main(int argc, char *argv[])
+{
+    QApplication app(argc, argv);
+
+    Q_INIT_RESOURCE(icons);
+    Q_INIT_RESOURCE(other);
+
+    return 0;
+}
diff --git a/workspace/sconstools3/qt6/test/qrc/multifiles/image/other.qrc b/workspace/sconstools3/qt6/test/qrc/multifiles/image/other.qrc
new file mode 100644
index 0000000..12ed5e8
--- /dev/null
+++ b/workspace/sconstools3/qt6/test/qrc/multifiles/image/other.qrc
@@ -0,0 +1,5 @@
+<!DOCTYPE RCC><RCC version="1.0">
+<qresource prefix="/">
+    <file>other/rocks.png</file>
+</qresource>
+</RCC>
diff --git a/workspace/sconstools3/qt6/test/qrc/multifiles/image/other/rocks.png b/workspace/sconstools3/qt6/test/qrc/multifiles/image/other/rocks.png
new file mode 100644
index 0000000..7d85f1f
Binary files /dev/null and b/workspace/sconstools3/qt6/test/qrc/multifiles/image/other/rocks.png differ
diff --git a/workspace/sconstools3/qt6/test/qrc/multifiles/sconstest-multifiles-manual.py b/workspace/sconstools3/qt6/test/qrc/multifiles/sconstest-multifiles-manual.py
new file mode 100644
index 0000000..ad54686
--- /dev/null
+++ b/workspace/sconstools3/qt6/test/qrc/multifiles/sconstest-multifiles-manual.py
@@ -0,0 +1,45 @@
+#!/usr/bin/env python
+#
+# Copyright (c) 2001-2010,2011,2012 The SCons Foundation
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+
+"""
+Tests the Qrc() builder, when more than one .qrc file is given.
+"""
+
+import TestSCons
+
+test = TestSCons.TestSCons()
+test.dir_fixture("image")
+test.file_fixture('SConscript-manual','SConscript')
+test.file_fixture('../../qtenv.py')
+test.file_fixture('../../../__init__.py','site_scons/site_tools/qt5/__init__.py')
+test.run()
+
+test.pass_test()
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/workspace/sconstools3/qt6/test/qrc/multifiles/sconstest-multifiles.py b/workspace/sconstools3/qt6/test/qrc/multifiles/sconstest-multifiles.py
new file mode 100644
index 0000000..ca41695
--- /dev/null
+++ b/workspace/sconstools3/qt6/test/qrc/multifiles/sconstest-multifiles.py
@@ -0,0 +1,46 @@
+#!/usr/bin/env python
+#
+# Copyright (c) 2001-2010,2011,2012 The SCons Foundation
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+
+"""
+Tests the Qrc() builder, when more than one .qrc file is given to the 
+Program/Library builders directly.
+"""
+
+import TestSCons
+
+test = TestSCons.TestSCons()
+test.dir_fixture("image")
+test.file_fixture('SConscript')
+test.file_fixture('../../qtenv.py')
+test.file_fixture('../../../__init__.py','site_scons/site_tools/qt5/__init__.py')
+test.run()
+
+test.pass_test()
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/workspace/sconstools3/qt6/test/qrc/othername/image/SConscript b/workspace/sconstools3/qt6/test/qrc/othername/image/SConscript
new file mode 100644
index 0000000..560f0cc
--- /dev/null
+++ b/workspace/sconstools3/qt6/test/qrc/othername/image/SConscript
@@ -0,0 +1,11 @@
+Import('qtEnv')
+
+env = qtEnv.Clone()
+env.EnableQt5Modules(['QtCore','QtWidgets'])
+
+source_files = Glob('*.cpp')
+source_files.append(env.Qrc5('icons'))
+
+env['QT5_QRCFLAGS'] = ['-name', 'othername']
+
+env.Program('main', source_files)
diff --git a/workspace/sconstools3/qt6/test/qrc/othername/image/SConstruct b/workspace/sconstools3/qt6/test/qrc/othername/image/SConstruct
new file mode 100644
index 0000000..00e5705
--- /dev/null
+++ b/workspace/sconstools3/qt6/test/qrc/othername/image/SConstruct
@@ -0,0 +1,6 @@
+import qtenv
+
+qtEnv = qtenv.createQtEnvironment()
+Export('qtEnv')
+SConscript('SConscript')
+    
\ No newline at end of file
diff --git a/workspace/sconstools3/qt6/test/qrc/othername/image/icons.qrc b/workspace/sconstools3/qt6/test/qrc/othername/image/icons.qrc
new file mode 100644
index 0000000..86fe71b
--- /dev/null
+++ b/workspace/sconstools3/qt6/test/qrc/othername/image/icons.qrc
@@ -0,0 +1,5 @@
+<!DOCTYPE RCC><RCC version="1.0">
+<qresource prefix="/">
+    <file>icons/scons.png</file>
+</qresource>
+</RCC>
diff --git a/workspace/sconstools3/qt6/test/qrc/othername/image/icons/scons.png b/workspace/sconstools3/qt6/test/qrc/othername/image/icons/scons.png
new file mode 100644
index 0000000..7d85f1f
Binary files /dev/null and b/workspace/sconstools3/qt6/test/qrc/othername/image/icons/scons.png differ
diff --git a/workspace/sconstools3/qt6/test/qrc/othername/image/main.cpp b/workspace/sconstools3/qt6/test/qrc/othername/image/main.cpp
new file mode 100644
index 0000000..0457bcd
--- /dev/null
+++ b/workspace/sconstools3/qt6/test/qrc/othername/image/main.cpp
@@ -0,0 +1,11 @@
+#include <QtWidgets/QApplication>
+#include <QtCore>
+
+int main(int argc, char *argv[])
+{
+    QApplication app(argc, argv);
+
+    Q_INIT_RESOURCE(othername);
+
+    return 0;
+}
diff --git a/workspace/sconstools3/qt6/test/qrc/othername/sconstest-othername.py b/workspace/sconstools3/qt6/test/qrc/othername/sconstest-othername.py
new file mode 100644
index 0000000..a62937d
--- /dev/null
+++ b/workspace/sconstools3/qt6/test/qrc/othername/sconstest-othername.py
@@ -0,0 +1,45 @@
+#!/usr/bin/env python
+#
+# Copyright (c) 2001-2010,2011,2012 The SCons Foundation
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+
+"""
+Rename test for the Qrc() builder, uses the '-name' flag to set a different
+name for the resulting .cxx file.
+"""
+
+import TestSCons
+
+test = TestSCons.TestSCons()
+test.dir_fixture("image")
+test.file_fixture('../../qtenv.py')
+test.file_fixture('../../../__init__.py','site_scons/site_tools/qt5/__init__.py')
+test.run()
+
+test.pass_test()
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/workspace/sconstools3/qt6/test/qrc/samefilename/image/SConscript b/workspace/sconstools3/qt6/test/qrc/samefilename/image/SConscript
new file mode 100644
index 0000000..1bcb156
--- /dev/null
+++ b/workspace/sconstools3/qt6/test/qrc/samefilename/image/SConscript
@@ -0,0 +1,8 @@
+Import('qtEnv')
+
+env = qtEnv.Clone()
+env.EnableQt5Modules(['QtCore','QtWidgets'])
+
+source_files = Glob('*.cpp')+Glob('*.qrc')
+
+env.Program('main', source_files)
diff --git a/workspace/sconstools3/qt6/test/qrc/samefilename/image/SConstruct b/workspace/sconstools3/qt6/test/qrc/samefilename/image/SConstruct
new file mode 100644
index 0000000..00e5705
--- /dev/null
+++ b/workspace/sconstools3/qt6/test/qrc/samefilename/image/SConstruct
@@ -0,0 +1,6 @@
+import qtenv
+
+qtEnv = qtenv.createQtEnvironment()
+Export('qtEnv')
+SConscript('SConscript')
+    
\ No newline at end of file
diff --git a/workspace/sconstools3/qt6/test/qrc/samefilename/image/icons.cpp b/workspace/sconstools3/qt6/test/qrc/samefilename/image/icons.cpp
new file mode 100644
index 0000000..304dc3a
--- /dev/null
+++ b/workspace/sconstools3/qt6/test/qrc/samefilename/image/icons.cpp
@@ -0,0 +1,11 @@
+#include <QtWidgets/QApplication>
+#include <QtCore>
+
+int main(int argc, char *argv[])
+{
+    QApplication app(argc, argv);
+
+    Q_INIT_RESOURCE(icons);
+
+    return 0;
+}
diff --git a/workspace/sconstools3/qt6/test/qrc/samefilename/image/icons.qrc b/workspace/sconstools3/qt6/test/qrc/samefilename/image/icons.qrc
new file mode 100644
index 0000000..86fe71b
--- /dev/null
+++ b/workspace/sconstools3/qt6/test/qrc/samefilename/image/icons.qrc
@@ -0,0 +1,5 @@
+<!DOCTYPE RCC><RCC version="1.0">
+<qresource prefix="/">
+    <file>icons/scons.png</file>
+</qresource>
+</RCC>
diff --git a/workspace/sconstools3/qt6/test/qrc/samefilename/image/icons/scons.png b/workspace/sconstools3/qt6/test/qrc/samefilename/image/icons/scons.png
new file mode 100644
index 0000000..7d85f1f
Binary files /dev/null and b/workspace/sconstools3/qt6/test/qrc/samefilename/image/icons/scons.png differ
diff --git a/workspace/sconstools3/qt6/test/qrc/samefilename/sconstest-samefilename.py b/workspace/sconstools3/qt6/test/qrc/samefilename/sconstest-samefilename.py
new file mode 100644
index 0000000..91178ac
--- /dev/null
+++ b/workspace/sconstools3/qt6/test/qrc/samefilename/sconstest-samefilename.py
@@ -0,0 +1,46 @@
+#!/usr/bin/env python
+#
+# Copyright (c) 2001-2010,2011,2012 The SCons Foundation
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+
+"""
+Check that the correct prefixes/suffixes are appended to the target of
+the Qrc() builder. Else, we could not add .qrc and CXX files with the
+same prefix to the Program/Library builders.
+"""
+
+import TestSCons
+
+test = TestSCons.TestSCons()
+test.dir_fixture("image")
+test.file_fixture('../../qtenv.py')
+test.file_fixture('../../../__init__.py','site_scons/site_tools/qt5/__init__.py')
+test.run()
+
+test.pass_test()
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/workspace/sconstools3/qt6/test/qrc/subdir/image/SConscript b/workspace/sconstools3/qt6/test/qrc/subdir/image/SConscript
new file mode 100644
index 0000000..53ef724
--- /dev/null
+++ b/workspace/sconstools3/qt6/test/qrc/subdir/image/SConscript
@@ -0,0 +1,8 @@
+Import('qtEnv')
+
+env = qtEnv.Clone()
+env.EnableQt5Modules(['QtCore','QtWidgets'])
+
+source_files = Glob('*.cpp')+['qrc/icons.qrc']
+
+env.Program('main', source_files)
diff --git a/workspace/sconstools3/qt6/test/qrc/subdir/image/SConstruct b/workspace/sconstools3/qt6/test/qrc/subdir/image/SConstruct
new file mode 100644
index 0000000..00e5705
--- /dev/null
+++ b/workspace/sconstools3/qt6/test/qrc/subdir/image/SConstruct
@@ -0,0 +1,6 @@
+import qtenv
+
+qtEnv = qtenv.createQtEnvironment()
+Export('qtEnv')
+SConscript('SConscript')
+    
\ No newline at end of file
diff --git a/workspace/sconstools3/qt6/test/qrc/subdir/image/main.cpp b/workspace/sconstools3/qt6/test/qrc/subdir/image/main.cpp
new file mode 100644
index 0000000..304dc3a
--- /dev/null
+++ b/workspace/sconstools3/qt6/test/qrc/subdir/image/main.cpp
@@ -0,0 +1,11 @@
+#include <QtWidgets/QApplication>
+#include <QtCore>
+
+int main(int argc, char *argv[])
+{
+    QApplication app(argc, argv);
+
+    Q_INIT_RESOURCE(icons);
+
+    return 0;
+}
diff --git a/workspace/sconstools3/qt6/test/qrc/subdir/image/qrc/icons.qrc b/workspace/sconstools3/qt6/test/qrc/subdir/image/qrc/icons.qrc
new file mode 100644
index 0000000..86fe71b
--- /dev/null
+++ b/workspace/sconstools3/qt6/test/qrc/subdir/image/qrc/icons.qrc
@@ -0,0 +1,5 @@
+<!DOCTYPE RCC><RCC version="1.0">
+<qresource prefix="/">
+    <file>icons/scons.png</file>
+</qresource>
+</RCC>
diff --git a/workspace/sconstools3/qt6/test/qrc/subdir/image/qrc/icons/scons.png b/workspace/sconstools3/qt6/test/qrc/subdir/image/qrc/icons/scons.png
new file mode 100644
index 0000000..7d85f1f
Binary files /dev/null and b/workspace/sconstools3/qt6/test/qrc/subdir/image/qrc/icons/scons.png differ
diff --git a/workspace/sconstools3/qt6/test/qrc/subdir/sconstest-subdir.py b/workspace/sconstools3/qt6/test/qrc/subdir/sconstest-subdir.py
new file mode 100644
index 0000000..bd03a96
--- /dev/null
+++ b/workspace/sconstools3/qt6/test/qrc/subdir/sconstest-subdir.py
@@ -0,0 +1,46 @@
+#!/usr/bin/env python
+#
+# Copyright (c) 2001-2010,2011,2012 The SCons Foundation
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+
+"""
+In this test the QRC file is placed in a subfolder (qrc/icons.qrc). The
+Qrc5() builder should correctly strip the leading path and set the "-name"
+option for the RCC executable to "icons" only.
+"""
+
+import TestSCons
+
+test = TestSCons.TestSCons()
+test.dir_fixture("image")
+test.file_fixture('../../qtenv.py')
+test.file_fixture('../../../__init__.py','site_scons/site_tools/qt5/__init__.py')
+test.run()
+
+test.pass_test()
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/workspace/sconstools3/qt6/test/qt_examples/create_scons_tests.py b/workspace/sconstools3/qt6/test/qt_examples/create_scons_tests.py
new file mode 100644
index 0000000..0ffbf02
--- /dev/null
+++ b/workspace/sconstools3/qt6/test/qt_examples/create_scons_tests.py
@@ -0,0 +1,748 @@
+#!/usr/bin/env python
+#
+# Copyright (c) 2001-2010,2011,2012 The SCons Foundation
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+#
+# create_scons_test.py
+#
+# Populates a Qt "examples" directory with files for the
+# SCons test framework.
+#
+#
+#
+# Usage:
+#
+# Step 1: Copy the "examples" folder of your Qt source tree into
+#         this directory.
+# Step 2: Run "python create_scons_tests.py" to create all files
+#         for the SCons test framework.
+# Step 3: Execute "python runtest.py -a" to run all tests
+#
+# Additional options for this script are:
+#
+#   -local               Creates the test files in the local directory,
+#                        also copies qtenv.py and qt5.py to their correct
+#                        places.
+#   -clean               Removes all intermediate test files.
+#
+#
+
+import os, sys, re, glob, shutil
+
+# cxx file extensions
+cxx_ext = ['.c', '.cpp', '.cxx', '.cc']
+# header file extensions
+h_ext = ['.h', '.hpp', '.hxx']
+# regex for includes
+inc_re = re.compile(r'#include\s+<([^>]+)>')
+# regex for qtLibraryTarget function
+qtlib_re = re.compile(r'\$\$qtLibraryTarget\(([^\)]+)\)')
+qrcinit_re = re.compile('Q_INIT_RESOURCE\(([^\)]+)\)')
+localvar_re = re.compile("\\$\\$([^/\s]+)")
+qthave_re = re.compile("qtHaveModule\([^\)]+\)\s*:\s")
+
+updir = '..'+os.sep
+
+# we currently skip all .pro files that use these config values
+complicated_configs = ['qdbus','phonon','plugin']
+# for the following CONFIG values we have to provide default qt modules
+config_modules = {'designer' : ['QtCore','QtGui','QtXml','QtWidgets','QtDesigner','QtDesignerComponents'],
+                  'uitools' : ['QtCore','QtGui','QtUiTools'],
+                  'assistant' : ['QtCore','QtGui','QtXml','QtScript','QtAssistant'],
+                  'core' : ['QtCore'],
+                  'gui' : ['QtCore', 'QtGui'],
+                  'concurrent' : ['QtCore', 'QtConcurrent'],
+                  'dbus' : ['QtCore', 'QtDBus'],
+                  'declarative' : ['QtCore', 'QtGui', 'QtScript', 'QtSql', 'QtNetwork', 'QtWidgets', 'QtXmlPatterns', 'QtDeclarative'],
+                  'printsupport' : ['QtCore', 'QtGui', 'QtWidgets', 'QtPrintSupport'],
+                  'mediawidgets' : ['QtCore', 'QtGui', 'QtOpenGL', 'QtNetwork', 'QtWidgets', 'QtMultimedia', 'QtMultimediaWidgets'],
+                  'webkitwidgets' : ['QtCore', 'QtGui', 'QtOpenGL', 'QtNetwork', 'QtWidgets', 'QtPrintSupport', 'QtWebKit', 'QtQuick', 'QtQml', 'QtSql', 'QtV8', 'QtWebKitWidgets'],
+                  'qml' : ['QtCore', 'QtGui', 'QtNetwork', 'QtV8', 'QtQml'],
+                  'quick' : ['QtCore', 'QtGui', 'QtNetwork', 'QtV8', 'QtQml', 'QtQuick'],
+                  'axcontainer' : [],
+                  'axserver' : [],
+                  'testlib' : ['QtCore', 'QtTest'],
+                  'xmlpatterns' : ['QtCore', 'QtNetwork', 'QtXmlPatterns'],
+                  'qt' : ['QtCore','QtGui'],
+                  'xml' : ['QtCore','QtGui','QtXml'],
+                  'webkit' : ['QtCore','QtGui','QtQuick','QtQml','QtNetwork','QtSql','QtV8','QtWebKit'],
+                  'network' : ['QtCore','QtNetwork'],
+                  'svg' : ['QtCore','QtGui','QtWidgets','QtSvg'],
+                  'script' : ['QtCore','QtScript'],
+                  'scripttools' : ['QtCore','QtGui','QtWidgets','QtScript','QtScriptTools'],
+                  'multimedia' : ['QtCore','QtGui','QtNetwork','QtMultimedia'],
+                  'script' : ['QtCore','QtScript'],
+                  'help' : ['QtCore','QtGui','QtWidgets','QtCLucene','QtSql','QtNetwork','QtHelp'],
+                  'qtestlib' : ['QtCore','QtTest'],
+                  'opengl' : ['QtCore','QtGui','QtOpenGL'],
+                  'widgets' : ['QtCore','QtGui','QtWidgets']
+                  }
+# for the following CONFIG values we have to provide additional CPP defines
+config_defines = {'plugin' : ['QT_PLUGIN'],
+                  'designer' : ['QDESIGNER_EXPORT_WIDGETS']
+                 }
+
+# dictionary of special Qt Environment settings for all single tests/pro files
+qtenv_flags = {'QT5_GOBBLECOMMENTS' : '1'
+              }
+
+# available qt modules
+validModules = [
+    # Qt Essentials
+    'QtCore',
+    'QtGui',
+    'QtMultimedia',
+    'QtMultimediaQuick_p',
+    'QtMultimediaWidgets',
+    'QtNetwork',
+    'QtPlatformSupport',
+    'QtQml',
+    'QtQmlDevTools',
+    'QtQuick',
+    'QtQuickParticles',
+    'QtSql',
+    'QtQuickTest',
+    'QtTest',
+    'QtWebKit',
+    'QtWebKitWidgets',
+    'QtWidgets',
+    # Qt Add-Ons
+    'QtConcurrent',
+    'QtDBus',
+    'QtOpenGL',
+    'QtPrintSupport',
+    'QtDeclarative',
+    'QtScript',
+    'QtScriptTools',
+    'QtSvg',
+    'QtUiTools',
+    'QtXml',
+    'QtXmlPatterns',
+    # Qt Tools
+    'QtHelp',
+    'QtDesigner',
+    'QtDesignerComponents',
+    # Other
+    'QtCLucene',
+    'QtConcurrent',
+    'QtV8'
+    ]
+
+def findQtBinParentPath(qtpath):
+    """ Within the given 'qtpath', search for a bin directory
+        containing the 'lupdate' executable and return
+        its parent path.
+    """
+    for path, dirs, files in os.walk(qtpath):
+        for d in dirs:
+            if d == 'bin':
+                if sys.platform.startswith("linux"):
+                    lpath = os.path.join(path, d, 'lupdate')
+                else:
+                    lpath = os.path.join(path, d, 'lupdate.exe')
+                if os.path.isfile(lpath):
+                    return path
+                
+    return ""
+
+def findMostRecentQtPath(dpath):
+    paths = glob.glob(dpath)
+    if len(paths):
+        paths.sort()
+        return findQtBinParentPath(paths[-1])
+        
+    return ""
+
+def detectLatestQtVersion():
+    if sys.platform.startswith("linux"):
+        # Inspect '/usr/local/Qt' first...
+        p = findMostRecentQtPath(os.path.join('/usr','local','Qt-*'))
+        if not p:
+            # ... then inspect '/usr/local/Trolltech'...
+            p = findMostRecentQtPath(os.path.join('/usr','local','Trolltech','*'))
+        if not p:
+            # ...then try to find a binary install...
+            p = findMostRecentQtPath(os.path.join('/opt','Qt*'))
+        if not p:
+            # ...then try to find a binary SDK.
+            p = findMostRecentQtPath(os.path.join('/opt','qtsdk*'))
+            
+    else:
+        # Simple check for Windows: inspect only 'C:\Qt'
+        paths = glob.glob(os.path.join('C:\\', 'Qt', 'Qt*'))
+        p = ""
+        if len(paths):
+            paths.sort()
+            # Select version with highest release number
+            paths = glob.glob(os.path.join(paths[-1], '5*'))
+            if len(paths):
+                paths.sort()
+                # Is it a MinGW or VS installation?
+                p = findMostRecentQtPath(os.path.join(paths[-1], 'mingw*'))
+                if not p:
+                    # No MinGW, so try VS...
+                    p = findMostRecentQtPath(os.path.join(paths[-1], 'msvc*'))
+        
+    return os.environ.get("QT5DIR", p)
+
+def detectPkgconfigPath(qtdir):
+    pkgpath = os.path.join(qtdir, 'lib', 'pkgconfig')
+    if os.path.exists(os.path.join(pkgpath,'Qt5Core.pc')):
+        return pkgpath
+    pkgpath = os.path.join(qtdir, 'lib')
+    if os.path.exists(os.path.join(pkgpath,'Qt5Core.pc')):
+        return pkgpath
+
+    return ""
+
+def expandProFile(fpath):
+    """ Read the given file into a list of single lines,
+        while expanding included files (mainly *.pri)
+        recursively.
+    """
+    lines = []
+    f = open(fpath,'r')
+    content = f.readlines()
+    f.close()
+    pwdhead, tail = os.path.split(fpath)
+    head = pwdhead
+    if pwdhead:
+        pwdhead = os.path.abspath(pwdhead)
+    else:
+        pwdhead = os.path.abspath(os.getcwd())
+    for idx, l in enumerate(content):
+        l = l.rstrip('\n')
+        l = l.rstrip()
+        if '$$PWD' in l:
+            l = l.replace("$$PWD", pwdhead)
+        if l.startswith('include('):
+            # Expand include file
+            l = l.rstrip(')')
+            l = l.replace('include(','')
+            while '$$' in l:
+                # Try to replace the used local variable by
+                # searching back in the content
+                m = localvar_re.search(l)
+                if m:
+                    key = m.group(1)
+                    tidx = idx-1
+                    skey = "%s = " % key
+                    while tidx >= 0:
+                        if content[tidx].startswith(skey):
+                            # Key found
+                            l = l.replace('$$%s' % key, content[tidx][len(skey):].rstrip('\n'))
+                            if os.path.join('..','..','qtbase','examples') in l:
+                                # Quick hack to cope with erroneous *.pro files
+                                l = l.replace(os.path.join('..','..','qtbase','examples'), os.path.join('..','examples'))
+                            break
+                        tidx -= 1
+                    if tidx < 0:
+                        print "Error: variable %s could not be found during parsing!" % key
+                        break
+            ipath = l
+            if head:
+                ipath = os.path.join(head, l)
+            # Does the file exist?
+            if not os.path.isfile(ipath):
+                # Then search for it the hard way
+                ihead, tail = os.path.split(ipath)
+                for spath, dirs, files in os.walk('.'):
+                    for f in files:
+                        if f == tail:
+                            ipath = os.path.join(spath, f)
+            lines.extend(expandProFile(ipath))
+        else:
+            lines.append(l)
+            
+    return lines
+
+def parseProFile(fpath):
+    """ Parse the .pro file fpath and return the defined
+    variables in a dictionary.
+    """
+    keys = {}
+    curkey = None
+    curlist = []
+    for l in expandProFile(fpath):
+        # Strip off qtHaveModule part
+        m = qthave_re.search(l)
+        if m:
+            l = qthave_re.sub("", l)
+        kl = l.split('=')
+        if len(kl) > 1:
+            # Close old key
+            if curkey:
+                if keys.has_key(curkey):
+                    keys[curkey].extend(curlist)
+                else:
+                    keys[curkey] = curlist
+                
+            # Split off trailing +
+            nkey = kl[0].rstrip('+')
+            nkey = nkey.rstrip()
+            # Split off optional leading part with "contains():"
+            cl = nkey.split(':')
+            if ('lesock' not in l) and ((len(cl) < 2) or ('msvc' in cl[0])):
+                nkey = cl[-1]
+                # Open new key
+                curkey = nkey.split()[0]
+                value = kl[1].lstrip()
+                if value.endswith('\\'):
+                    # Key is continued on next line
+                    value = value[:-1]
+                    curlist = value.split()
+                else:
+                    # Single line key
+                    if keys.has_key(curkey):
+                        keys[curkey].extend(value.split())
+                    else:
+                        keys[curkey] = value.split()
+                    curkey = None
+                    curlist = []
+        else:
+            if l.endswith('\\'):
+                # Continue current key
+                curlist.extend(l[:-1].split())
+            else:
+                # Unknown, so go back to VOID state
+                if curkey:
+                    # Append last item for current key...
+                    curlist.extend(l.split())
+                    if keys.has_key(curkey):
+                        keys[curkey].extend(curlist)
+                    else:
+                        keys[curkey] = curlist
+                        
+                    # ... and reset parse state.
+                    curkey = None
+                    curlist = []
+    
+    return keys
+
+def writeSConstruct(dirpath):
+    """ Create a SConstruct file in dirpath.
+    """
+    sc = open(os.path.join(dirpath,'SConstruct'),'w')
+    sc.write("""import qtenv
+
+qtEnv = qtenv.createQtEnvironment()
+Export('qtEnv')
+SConscript('SConscript')
+    """)
+    sc.close()
+
+def collectModulesFromFiles(fpath):
+    """ Scan source files in dirpath for included
+    Qt5 modules, and return the used modules in a list.
+    """
+    mods = []
+    try:
+        f = open(fpath,'r')
+        content = f.read()
+        f.close()
+    except:
+        return mods
+    
+    for m in inc_re.finditer(content):
+        mod = m.group(1)
+        if (mod in validModules) and (mod not in mods):
+            mods.append(mod)
+    return mods
+
+def findQResourceName(fpath):
+    """ Scan the source file fpath and return the name
+        of the QRC instance that gets initialized via
+        QRC_INIT_RESOURCE.
+    """
+    name = ""
+    
+    try:
+        f = open(fpath,'r')
+        content = f.read()
+        f.close()
+    except:
+        return name
+    
+    m = qrcinit_re.search(content)
+    if m:
+        name = m.group(1)
+    
+    return name
+
+def validKey(key, pkeys):
+    """ Helper function
+    """
+    if pkeys.has_key(key) and len(pkeys[key]) > 0:
+        return True
+    
+    return False
+
+def collectModules(dirpath, pkeys):
+    """ Scan source files in dirpath for included
+    Qt5 modules, and return the used modules in a list.
+    """
+    mods = []
+    defines = []
+    # Scan subdirs
+    if validKey('SUBDIRS', pkeys):
+        for s in pkeys['SUBDIRS']:
+            flist = glob.glob(os.path.join(dirpath, s, '*.*'))
+            for f in flist:
+                root, ext = os.path.splitext(f)
+                if (ext and ((ext in cxx_ext) or
+                             (ext in h_ext))):
+                    mods.extend(collectModulesFromFiles(f))
+                    
+    # Scan sources
+    if validKey('SOURCES', pkeys):
+        for s in pkeys['SOURCES']:
+            f = os.path.join(dirpath,s)
+            mods.extend(collectModulesFromFiles(f))
+    
+    # Scan headers
+    if validKey('HEADERS', pkeys):
+        for s in pkeys['HEADERS']:
+            f = os.path.join(dirpath,s)
+            mods.extend(collectModulesFromFiles(f))
+            
+    # Check CONFIG keyword
+    if validKey('CONFIG', pkeys):
+        for k in pkeys['CONFIG']:
+            if config_modules.has_key(k):
+                mods.extend(config_modules[k])
+            if config_defines.has_key(k):
+                defines.extend(config_defines[k])
+
+    # Check QT keyword
+    if validKey('QT', pkeys):
+        for k in pkeys['QT']:
+            if config_modules.has_key(k):
+                mods.extend(config_modules[k])
+
+    # Make lists unique
+    unique_mods = []
+    for m in mods:
+        if m not in unique_mods:
+            unique_mods.append(m)
+    unique_defines = []
+    for d in defines:
+        if d not in unique_defines:
+            unique_defines.append(d)
+
+    # Safety hack, if no modules are found so far
+    # assume that this is a normal Qt GUI application...
+    if len(unique_mods) == 0:
+        unique_mods = ['QtCore','QtGui']
+
+    return (unique_mods, unique_defines)
+
+def relOrAbsPath(dirpath, rpath):
+    if rpath.startswith('..'):
+        return os.path.abspath(os.path.normpath(os.path.join(dirpath, rpath)))
+
+    return rpath
+
+def writeSConscript(dirpath, profile, pkeys):
+    """ Create a SConscript file in dirpath.
+    """
+
+    # Activate modules
+    mods, defines = collectModules(dirpath, pkeys)
+    if validKey('CONFIG', pkeys) and isComplicated(pkeys['CONFIG'][0]):
+        return False
+    
+    qrcname = ""
+    if not validKey('SOURCES', pkeys):
+        # No SOURCES specified, try to find CPP files
+        slist = glob.glob(os.path.join(dirpath,'*.cpp'))
+        if len(slist) == 0:
+            # Nothing to build here
+            return False
+        else:
+            # Scan for Q_INIT_RESOURCE
+            for s in slist:
+                qrcname = findQResourceName(s)
+                if qrcname:
+                    break   
+    
+    allmods = True
+    for m in mods:
+        if m not in pkeys['qtmodules']:
+            print "   no module %s" % m
+            allmods = False
+    if not allmods:
+        return False
+
+    sc = open(os.path.join(dirpath,'SConscript'),'w')
+    sc.write("""Import('qtEnv')
+
+env = qtEnv.Clone()
+""")
+
+    if len(mods):
+        sc.write('env.EnableQt5Modules([\n')
+        for m in mods[:-1]:
+            sc.write("'%s',\n" % m)
+        sc.write("'%s'\n" % mods[-1])
+        sc.write('])\n\n')
+
+    # Add CPPDEFINEs
+    if len(defines):
+        sc.write('env.AppendUnique(CPPDEFINES=[\n')
+        for d in defines[:-1]:
+            sc.write("'%s',\n" % d)
+        sc.write("'%s'\n" % defines[-1])
+        sc.write('])\n\n')
+    
+    # Add LIBS
+    if validKey('LIBS', pkeys):
+        sc.write('env.AppendUnique(LIBS=[\n')
+        for d in pkeys['LIBS'][:-1]:
+            sc.write("'%s',\n" % d)
+        sc.write("'%s'\n" % pkeys['LIBS'][-1])
+        sc.write('])\n\n')
+
+    # Collect INCLUDEPATHs
+    incpaths = []
+    if validKey('INCLUDEPATH', pkeys):
+        incpaths = pkeys['INCLUDEPATH']
+    if validKey('FORMS', pkeys):
+        for s in pkeys['FORMS']:
+            head, tail = os.path.split(s)
+            if head and head not in incpaths:
+                incpaths.append(head)
+    if incpaths:
+        sc.write('env.Append(CPPPATH=[\n')
+        for d in incpaths[:-1]:
+            sc.write("'%s',\n" % relOrAbsPath(dirpath, d))
+        sc.write("'%s'\n" % relOrAbsPath(dirpath, incpaths[-1]))
+        sc.write('])\n\n')
+  
+    # Add special environment flags
+    if len(qtenv_flags):
+        for key, value in qtenv_flags.iteritems():    
+            sc.write("env['%s']=%s\n" % (key, value))
+    
+    
+    # Write source files
+    if validKey('SOURCES', pkeys):
+        sc.write('source_files = [\n')
+        for s in pkeys['SOURCES'][:-1]:
+            sc.write("'%s',\n" % relOrAbsPath(dirpath, s))
+            if not qrcname:
+                qrcname = findQResourceName(os.path.join(dirpath,s))
+
+        sc.write("'%s'\n" % relOrAbsPath(dirpath, pkeys['SOURCES'][-1]))
+        if not qrcname:
+            qrcname = findQResourceName(os.path.join(dirpath,pkeys['SOURCES'][-1]))
+        sc.write(']\n\n')
+    
+    # Write .ui files
+    if validKey('FORMS', pkeys):
+        sc.write('ui_files = [\n')
+        for s in pkeys['FORMS'][:-1]:
+            sc.write("'%s',\n" % relOrAbsPath(dirpath, s))
+        sc.write("'%s'\n" % relOrAbsPath(dirpath, pkeys['FORMS'][-1]))
+        sc.write(']\n')
+        sc.write('env.Uic5(ui_files)\n\n')
+    
+    # Write .qrc files
+    if validKey('RESOURCES', pkeys):
+        qrc_name = pkeys['RESOURCES'][0]
+        if qrcname:
+            if qrc_name.endswith('.qrc'):
+                qrc_name = qrc_name[:-4]
+            sc.write("qrc_out = env.Qrc5('%s')\nsource_files.append(qrc_out)\nenv['QT5_QRCFLAGS'] = ['-name', '%s']\n" % (qrc_name, qrcname))
+        else:
+            if not qrc_name.endswith('.qrc'):
+                qrc_name += '.qrc'
+            sc.write("source_files.append('%s')\n" % qrc_name)
+    
+    # Select module
+    type = 'Program'
+    if validKey('TEMPLATE', pkeys):
+        if pkeys['TEMPLATE'][0] == 'lib':
+            type = 'StaticLibrary'
+        if pkeys['TEMPLATE'][0] == 'dll':
+            type = 'SharedLibrary'
+
+    # TARGET may be wrapped by qtLibraryTarget function...    
+    target = profile
+    if validKey('TARGET', pkeys):
+        t = pkeys['TARGET'][0]
+        m = qtlib_re.search(t)
+        if m:
+            t = "Qt" + m.group(1)
+        target = t.replace("$$TARGET", profile)
+        
+    # Create program/lib/dll
+    else:
+        if validKey('SOURCES', pkeys):    
+            sc.write("env.%s('%s', source_files)\n\n" % (type, target))
+        else:
+            sc.write("env.%s('%s', Glob('*.cpp'))\n\n" % (type, target))
+        
+    sc.close()
+
+    return True
+
+def writeSConsTestFile(dirpath, folder):
+    dirnums = dirpath.count(os.sep)+1
+    f = open(os.path.join(dirpath, "sconstest-%s.py" % folder),'w')
+    f.write("""
+import os
+import TestSCons
+
+test = TestSCons.TestSCons()
+test.dir_fixture("%s")
+test.file_fixture('%sqtenv.py')
+test.file_fixture('%s__init__.py', os.path.join('site_scons','site_tools','qt5','__init__.py'))
+test.run()
+
+test.pass_test()
+    """ % (folder, updir*dirnums, updir*(dirnums+1)))
+    f.close()
+
+def installLocalFiles(dirpath):
+    dirnums = dirpath.count(os.sep)+1
+    shutil.copy(os.path.join(dirpath,updir*dirnums+'qtenv.py'),
+                os.path.join(dirpath,'qtenv.py'))
+    toolpath = os.path.join(dirpath,'site_scons','site_tools','qt5')
+    if not os.path.exists(toolpath):
+        os.makedirs(toolpath)
+    shutil.copy(os.path.join(dirpath,updir*(dirnums+1)+'__init__.py'),
+                os.path.join(dirpath,'site_scons','site_tools','qt5','__init__.py'))
+    
+def isComplicated(keyvalues):
+    for s in keyvalues:
+        if s in complicated_configs:
+            return True
+    
+    return False
+
+# Folders that should get skipped while creating the SConscripts
+skip_folders = ['activeqt', 'declarative', 'dbus']
+
+def createSConsTest(dirpath, profile, options):
+    """ Create files for the SCons test framework in dirpath.
+    """
+    pkeys = parseProFile(os.path.join(dirpath, profile))
+    if validKey('TEMPLATE', pkeys) and pkeys['TEMPLATE'][0] == 'subdirs':
+        return
+    if validKey('CONFIG', pkeys) and isComplicated(pkeys['CONFIG']):
+        return
+    if validKey('QT', pkeys) and isComplicated(pkeys['QT']):
+        return
+
+    head, tail = os.path.split(dirpath)
+    if head and tail:
+        print os.path.join(dirpath, profile)
+        for s in skip_folders:
+            if s in dirpath:
+                return
+        pkeys['qtmodules'] = options['qtmodules']
+        if not writeSConscript(dirpath, profile[:-4], pkeys):
+            return
+        writeSConstruct(dirpath)
+        writeSConsTestFile(head, tail)
+        if options['local']:
+            installLocalFiles(dirpath)
+
+def cleanSConsTest(dirpath, profile, options):
+    """ Remove files for the SCons test framework in dirpath.
+    """
+    try:
+        os.remove(os.path.join(dirpath,'SConstruct'))
+    except:
+        pass
+    try:
+        os.remove(os.path.join(dirpath,'SConscript'))
+    except:
+        pass
+    try:
+        os.remove(os.path.join(dirpath,'qtenv.py'))
+    except:
+        pass
+    try:
+        shutil.rmtree(os.path.join(dirpath,'site_scons'),
+                      ignore_errors=True)
+    except:
+        pass
+    head, tail = os.path.split(dirpath)
+    if head and tail:
+        try:
+            os.remove(os.path.join(head, "sconstest-%s.py" % tail))
+        except:
+            pass
+        
+def main():
+    """ The main program.
+    """
+
+    # Parse command line options
+    options = {'local' : False, # Install qtenv.py and qt5.py in local folder
+               'qtpath' : None,
+               'pkgconfig' : None
+               }
+    clean = False
+    qtpath = None
+    for o in sys.argv[1:]:
+        if o == "-local":
+            options['local'] = True
+        elif o == "-clean":
+            clean = True
+        else:
+            options['qtpath'] = o
+    
+    if not options['qtpath']:
+        qtpath = detectLatestQtVersion()
+        if qtpath == "":
+            print "No Qt installation found!"
+            sys.exit(1)
+
+        is_win = sys.platform.startswith('win')
+        if not is_win:
+            # Use pkgconfig to detect the available modules
+            options['pkgconfig'] = detectPkgconfigPath(qtpath)
+            if options['pkgconfig'] == "":
+                print "No pkgconfig files found!"
+                sys.exit(1)
+
+        options['qtpath'] = qtpath
+        options['qtmodules'] = []
+        for v in validModules:
+            if is_win or os.path.exists(os.path.join(options['pkgconfig'],v.replace('Qt','Qt5')+'.pc')):
+                options['qtmodules'].append(v)
+
+    if not clean:
+        doWork = createSConsTest
+    else:
+        doWork = cleanSConsTest
+        
+    # Detect .pro files
+    for path, dirs, files in os.walk('.'):
+        for f in files:
+            if f.endswith('.pro'):
+                doWork(path, f, options)
+
+if __name__ == "__main__":
+    main()
diff --git a/workspace/sconstools3/qt6/test/qt_examples/sconstest.skip b/workspace/sconstools3/qt6/test/qt_examples/sconstest.skip
new file mode 100644
index 0000000..e69de29
diff --git a/workspace/sconstools3/qt6/test/qtenv.py b/workspace/sconstools3/qt6/test/qtenv.py
new file mode 100644
index 0000000..f37faf2
--- /dev/null
+++ b/workspace/sconstools3/qt6/test/qtenv.py
@@ -0,0 +1,107 @@
+#
+# Copyright (c) 2001-2010 The SCons Foundation
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+
+import os, sys, glob
+from SCons import Environment
+
+def findQtBinParentPath(qtpath):
+    """ Within the given 'qtpath', search for a bin directory
+        containing the 'lupdate' executable and return
+        its parent path.
+    """
+    for path, dirs, files in os.walk(qtpath):
+        for d in dirs:
+            if d == 'bin':
+                if sys.platform.startswith("linux"):
+                    lpath = os.path.join(path, d, 'lupdate')
+                else:
+                    lpath = os.path.join(path, d, 'lupdate.exe')
+                if os.path.isfile(lpath):
+                    return path
+                
+    return ""
+
+def findMostRecentQtPath(dpath):
+    paths = glob.glob(dpath)
+    if len(paths):
+        paths.sort()
+        return findQtBinParentPath(paths[-1])
+        
+    return ""
+
+def detectLatestQtVersion():
+    if sys.platform.startswith("linux"):
+        # Inspect '/usr/local/Qt' first...
+        p = findMostRecentQtPath('/usr/local/Qt-*')
+        if not p:
+            # ... then inspect '/usr/local/Trolltech'...
+            p = findMostRecentQtPath('/usr/local/Trolltech/*')
+        if not p:
+            # ...then try to find a binary install...
+            p = findMostRecentQtPath('/opt/Qt*')
+        if not p:
+            # ...then try to find a binary SDK.
+            p = findMostRecentQtPath('/opt/qtsdk*')
+            
+    else:
+        # Simple check for Windows: inspect only 'C:\Qt'
+        paths = glob.glob(os.path.join('C:\\', 'Qt', 'Qt*'))
+        p = ""
+        if len(paths):
+            paths.sort()
+            # Select version with highest release number
+            paths = glob.glob(os.path.join(paths[-1], '5*'))
+            if len(paths):
+                paths.sort()
+                # Is it a MinGW or VS installation?
+                p = findMostRecentQtPath(os.path.join(paths[-1], 'mingw*'))
+                if not p:
+                    # No MinGW, so try VS...
+                    p = findMostRecentQtPath(os.path.join(paths[-1], 'msvc*'))
+        
+    return os.environ.get("QT5DIR", p)
+
+def detectPkgconfigPath(qtdir):
+    pkgpath = os.path.join(qtdir, 'lib', 'pkgconfig')
+    if os.path.exists(os.path.join(pkgpath,'Qt5Core.pc')):
+        return pkgpath
+    pkgpath = os.path.join(qtdir, 'lib')
+    if os.path.exists(os.path.join(pkgpath,'Qt5Core.pc')):
+        return pkgpath
+
+    return ""
+    
+def createQtEnvironment(qtdir=None, env=None):
+    if not env:
+        env = Environment.Environment(tools=['default'])
+    if not qtdir:
+        qtdir = detectLatestQtVersion()
+    env['QT5DIR'] = qtdir
+    if sys.platform.startswith("linux"):
+        env['ENV']['PKG_CONFIG_PATH'] = detectPkgconfigPath(qtdir)
+    env.Tool('qt5')
+    env.Append(CXXFLAGS=['-fPIC'])
+
+    return env
+
diff --git a/workspace/sconstools3/qt6/test/sconstest.skip b/workspace/sconstools3/qt6/test/sconstest.skip
new file mode 100644
index 0000000..e69de29
diff --git a/workspace/sconstools3/qt6/test/ts_qm/clean/image/MyFile.cpp b/workspace/sconstools3/qt6/test/ts_qm/clean/image/MyFile.cpp
new file mode 100644
index 0000000..09e27c6
--- /dev/null
+++ b/workspace/sconstools3/qt6/test/ts_qm/clean/image/MyFile.cpp
@@ -0,0 +1,7 @@
+#include "MyFile.h"
+
+aaa::aaa() : my_s(tr("SCons rocks!"))
+{
+  ;
+}
+
diff --git a/workspace/sconstools3/qt6/test/ts_qm/clean/image/MyFile.h b/workspace/sconstools3/qt6/test/ts_qm/clean/image/MyFile.h
new file mode 100644
index 0000000..10311dd
--- /dev/null
+++ b/workspace/sconstools3/qt6/test/ts_qm/clean/image/MyFile.h
@@ -0,0 +1,13 @@
+#include <QObject>
+#include <QString>
+
+class aaa : public QObject
+{
+  Q_OBJECT
+
+public:
+  aaa();
+
+private:
+  QString my_s;
+};
diff --git a/workspace/sconstools3/qt6/test/ts_qm/clean/image/SConscript b/workspace/sconstools3/qt6/test/ts_qm/clean/image/SConscript
new file mode 100644
index 0000000..915e1cd
--- /dev/null
+++ b/workspace/sconstools3/qt6/test/ts_qm/clean/image/SConscript
@@ -0,0 +1,6 @@
+Import("qtEnv")
+
+qtEnv['QT5_CLEAN_TS']=1
+
+qtEnv.Ts5('my_en.ts', Glob('*.cpp'))
+qtEnv.Qm5('my_en','my_en')
diff --git a/workspace/sconstools3/qt6/test/ts_qm/clean/image/SConstruct b/workspace/sconstools3/qt6/test/ts_qm/clean/image/SConstruct
new file mode 100644
index 0000000..d9d897d
--- /dev/null
+++ b/workspace/sconstools3/qt6/test/ts_qm/clean/image/SConstruct
@@ -0,0 +1,6 @@
+import qtenv
+
+qtEnv = qtenv.createQtEnvironment()
+Export('qtEnv')
+SConscript('SConscript')
+
diff --git a/workspace/sconstools3/qt6/test/ts_qm/clean/sconstest-clean.py b/workspace/sconstools3/qt6/test/ts_qm/clean/sconstest-clean.py
new file mode 100644
index 0000000..40cb442
--- /dev/null
+++ b/workspace/sconstools3/qt6/test/ts_qm/clean/sconstest-clean.py
@@ -0,0 +1,54 @@
+#!/usr/bin/env python
+#
+# Copyright (c) 2001-2010,2011,2012 The SCons Foundation
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+
+"""
+Test the QT5_CLEAN_TS option, which removes .ts files
+on a 'scons -c'.
+"""
+
+import TestSCons
+
+test = TestSCons.TestSCons()
+test.dir_fixture("image")
+test.file_fixture('../../qtenv.py')
+test.file_fixture('../../../__init__.py','site_scons/site_tools/qt5/__init__.py')
+test.run(stderr=None)
+
+test.must_exist(test.workpath('my_en.ts'))
+test.must_contain(test.workpath('my_en.ts'),'SCons rocks!')
+test.must_exist(test.workpath('my_en.qm'))
+
+test.run(options = '-c')
+
+test.must_not_exist(test.workpath('my_en.ts'))
+test.must_not_exist(test.workpath('my_en.qm'))
+
+test.pass_test()
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/workspace/sconstools3/qt6/test/ts_qm/mixdir/image/MyFile.cpp b/workspace/sconstools3/qt6/test/ts_qm/mixdir/image/MyFile.cpp
new file mode 100644
index 0000000..09e27c6
--- /dev/null
+++ b/workspace/sconstools3/qt6/test/ts_qm/mixdir/image/MyFile.cpp
@@ -0,0 +1,7 @@
+#include "MyFile.h"
+
+aaa::aaa() : my_s(tr("SCons rocks!"))
+{
+  ;
+}
+
diff --git a/workspace/sconstools3/qt6/test/ts_qm/mixdir/image/MyFile.h b/workspace/sconstools3/qt6/test/ts_qm/mixdir/image/MyFile.h
new file mode 100644
index 0000000..10311dd
--- /dev/null
+++ b/workspace/sconstools3/qt6/test/ts_qm/mixdir/image/MyFile.h
@@ -0,0 +1,13 @@
+#include <QObject>
+#include <QString>
+
+class aaa : public QObject
+{
+  Q_OBJECT
+
+public:
+  aaa();
+
+private:
+  QString my_s;
+};
diff --git a/workspace/sconstools3/qt6/test/ts_qm/mixdir/image/SConscript b/workspace/sconstools3/qt6/test/ts_qm/mixdir/image/SConscript
new file mode 100644
index 0000000..4b68448
--- /dev/null
+++ b/workspace/sconstools3/qt6/test/ts_qm/mixdir/image/SConscript
@@ -0,0 +1,3 @@
+Import("qtEnv")
+
+qtEnv.Ts5('my_en', ['MyFile.cpp','subdir'])
diff --git a/workspace/sconstools3/qt6/test/ts_qm/mixdir/image/SConstruct b/workspace/sconstools3/qt6/test/ts_qm/mixdir/image/SConstruct
new file mode 100644
index 0000000..d9d897d
--- /dev/null
+++ b/workspace/sconstools3/qt6/test/ts_qm/mixdir/image/SConstruct
@@ -0,0 +1,6 @@
+import qtenv
+
+qtEnv = qtenv.createQtEnvironment()
+Export('qtEnv')
+SConscript('SConscript')
+
diff --git a/workspace/sconstools3/qt6/test/ts_qm/mixdir/image/subdir/bbb.cpp b/workspace/sconstools3/qt6/test/ts_qm/mixdir/image/subdir/bbb.cpp
new file mode 100644
index 0000000..687c0cd
--- /dev/null
+++ b/workspace/sconstools3/qt6/test/ts_qm/mixdir/image/subdir/bbb.cpp
@@ -0,0 +1,6 @@
+#include "bbb.h"
+
+bbb::bbb() : my_s(tr("And Qt5 too!"))
+{
+  ;
+}
diff --git a/workspace/sconstools3/qt6/test/ts_qm/mixdir/image/subdir/bbb.h b/workspace/sconstools3/qt6/test/ts_qm/mixdir/image/subdir/bbb.h
new file mode 100644
index 0000000..0b60846
--- /dev/null
+++ b/workspace/sconstools3/qt6/test/ts_qm/mixdir/image/subdir/bbb.h
@@ -0,0 +1,13 @@
+#include <QObject>
+#include <QString>
+
+class bbb : public QObject
+{
+  Q_OBJECT
+
+public:
+  bbb();
+
+private:
+  QString my_s;
+};
diff --git a/workspace/sconstools3/qt6/test/ts_qm/mixdir/sconstest-mixdir.py b/workspace/sconstools3/qt6/test/ts_qm/mixdir/sconstest-mixdir.py
new file mode 100644
index 0000000..df16877
--- /dev/null
+++ b/workspace/sconstools3/qt6/test/ts_qm/mixdir/sconstest-mixdir.py
@@ -0,0 +1,49 @@
+#!/usr/bin/env python
+#
+# Copyright (c) 2001-2010,2011,2012 The SCons Foundation
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+
+"""
+Runs the Ts() builder with a mix of files and dirs as
+input (list of sources).
+"""
+
+import TestSCons
+
+test = TestSCons.TestSCons()
+test.dir_fixture("image")
+test.file_fixture('../../qtenv.py')
+test.file_fixture('../../../__init__.py','site_scons/site_tools/qt5/__init__.py')
+test.run(stderr=None)
+
+test.must_exist(test.workpath('my_en.ts'))
+test.must_contain(test.workpath('my_en.ts'),'SCons rocks!')
+test.must_contain(test.workpath('my_en.ts'),'And Qt5 too!')
+
+test.pass_test()
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/workspace/sconstools3/qt6/test/ts_qm/multisource/image/MyFile.cpp b/workspace/sconstools3/qt6/test/ts_qm/multisource/image/MyFile.cpp
new file mode 100644
index 0000000..09e27c6
--- /dev/null
+++ b/workspace/sconstools3/qt6/test/ts_qm/multisource/image/MyFile.cpp
@@ -0,0 +1,7 @@
+#include "MyFile.h"
+
+aaa::aaa() : my_s(tr("SCons rocks!"))
+{
+  ;
+}
+
diff --git a/workspace/sconstools3/qt6/test/ts_qm/multisource/image/MyFile.h b/workspace/sconstools3/qt6/test/ts_qm/multisource/image/MyFile.h
new file mode 100644
index 0000000..10311dd
--- /dev/null
+++ b/workspace/sconstools3/qt6/test/ts_qm/multisource/image/MyFile.h
@@ -0,0 +1,13 @@
+#include <QObject>
+#include <QString>
+
+class aaa : public QObject
+{
+  Q_OBJECT
+
+public:
+  aaa();
+
+private:
+  QString my_s;
+};
diff --git a/workspace/sconstools3/qt6/test/ts_qm/multisource/image/SConscript b/workspace/sconstools3/qt6/test/ts_qm/multisource/image/SConscript
new file mode 100644
index 0000000..677bd35
--- /dev/null
+++ b/workspace/sconstools3/qt6/test/ts_qm/multisource/image/SConscript
@@ -0,0 +1,6 @@
+Import("qtEnv")
+
+qtEnv.Ts5('my_en', Glob('*.cpp'))
+qtEnv.Ts5('a','MyFile.cpp')
+qtEnv.Ts5('b','bbb.cpp')
+qtEnv.Qm5('my_en',['a','b'])
diff --git a/workspace/sconstools3/qt6/test/ts_qm/multisource/image/SConstruct b/workspace/sconstools3/qt6/test/ts_qm/multisource/image/SConstruct
new file mode 100644
index 0000000..d9d897d
--- /dev/null
+++ b/workspace/sconstools3/qt6/test/ts_qm/multisource/image/SConstruct
@@ -0,0 +1,6 @@
+import qtenv
+
+qtEnv = qtenv.createQtEnvironment()
+Export('qtEnv')
+SConscript('SConscript')
+
diff --git a/workspace/sconstools3/qt6/test/ts_qm/multisource/image/bbb.cpp b/workspace/sconstools3/qt6/test/ts_qm/multisource/image/bbb.cpp
new file mode 100644
index 0000000..687c0cd
--- /dev/null
+++ b/workspace/sconstools3/qt6/test/ts_qm/multisource/image/bbb.cpp
@@ -0,0 +1,6 @@
+#include "bbb.h"
+
+bbb::bbb() : my_s(tr("And Qt5 too!"))
+{
+  ;
+}
diff --git a/workspace/sconstools3/qt6/test/ts_qm/multisource/image/bbb.h b/workspace/sconstools3/qt6/test/ts_qm/multisource/image/bbb.h
new file mode 100644
index 0000000..0b60846
--- /dev/null
+++ b/workspace/sconstools3/qt6/test/ts_qm/multisource/image/bbb.h
@@ -0,0 +1,13 @@
+#include <QObject>
+#include <QString>
+
+class bbb : public QObject
+{
+  Q_OBJECT
+
+public:
+  bbb();
+
+private:
+  QString my_s;
+};
diff --git a/workspace/sconstools3/qt6/test/ts_qm/multisource/sconstest-multisource.py b/workspace/sconstools3/qt6/test/ts_qm/multisource/sconstest-multisource.py
new file mode 100644
index 0000000..5b66c9a
--- /dev/null
+++ b/workspace/sconstools3/qt6/test/ts_qm/multisource/sconstest-multisource.py
@@ -0,0 +1,61 @@
+#!/usr/bin/env python
+#
+# Copyright (c) 2001-2010,2011,2012 The SCons Foundation
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+
+"""
+Tests that the Ts() and Qm() builders accept and
+process multiple sources correctly.
+"""
+
+import TestSCons
+
+test = TestSCons.TestSCons()
+test.dir_fixture("image")
+test.file_fixture('../../qtenv.py')
+test.file_fixture('../../../__init__.py','site_scons/site_tools/qt5/__init__.py')
+test.run(stderr=None)
+
+test.must_exist(test.workpath('my_en.ts'))
+test.must_exist(test.workpath('a.ts'))
+test.must_exist(test.workpath('b.ts'))
+test.must_contain(test.workpath('my_en.ts'),'SCons rocks!')
+test.must_contain(test.workpath('my_en.ts'),'And Qt5 too!')
+test.must_contain(test.workpath('a.ts'),'SCons rocks!')
+test.must_contain(test.workpath('b.ts'),'And Qt5 too!')
+test.must_exist(test.workpath('my_en.qm'))
+
+test.run(options = '-c')
+
+test.must_exist(test.workpath('my_en.ts'))
+test.must_exist(test.workpath('a.ts'))
+test.must_exist(test.workpath('b.ts'))
+test.must_not_exist(test.workpath('my_en.qm'))
+
+test.pass_test()
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/workspace/sconstools3/qt6/test/ts_qm/multitarget/image/MyFile.cpp b/workspace/sconstools3/qt6/test/ts_qm/multitarget/image/MyFile.cpp
new file mode 100644
index 0000000..09e27c6
--- /dev/null
+++ b/workspace/sconstools3/qt6/test/ts_qm/multitarget/image/MyFile.cpp
@@ -0,0 +1,7 @@
+#include "MyFile.h"
+
+aaa::aaa() : my_s(tr("SCons rocks!"))
+{
+  ;
+}
+
diff --git a/workspace/sconstools3/qt6/test/ts_qm/multitarget/image/MyFile.h b/workspace/sconstools3/qt6/test/ts_qm/multitarget/image/MyFile.h
new file mode 100644
index 0000000..10311dd
--- /dev/null
+++ b/workspace/sconstools3/qt6/test/ts_qm/multitarget/image/MyFile.h
@@ -0,0 +1,13 @@
+#include <QObject>
+#include <QString>
+
+class aaa : public QObject
+{
+  Q_OBJECT
+
+public:
+  aaa();
+
+private:
+  QString my_s;
+};
diff --git a/workspace/sconstools3/qt6/test/ts_qm/multitarget/image/SConscript b/workspace/sconstools3/qt6/test/ts_qm/multitarget/image/SConscript
new file mode 100644
index 0000000..0d6a666
--- /dev/null
+++ b/workspace/sconstools3/qt6/test/ts_qm/multitarget/image/SConscript
@@ -0,0 +1,4 @@
+Import("qtEnv")
+
+qtEnv.Ts5(['my_en','my_de'], Glob('*.cpp'))
+qtEnv.Qm5(['my_en','my_de'],'my_en')
diff --git a/workspace/sconstools3/qt6/test/ts_qm/multitarget/image/SConstruct b/workspace/sconstools3/qt6/test/ts_qm/multitarget/image/SConstruct
new file mode 100644
index 0000000..d9d897d
--- /dev/null
+++ b/workspace/sconstools3/qt6/test/ts_qm/multitarget/image/SConstruct
@@ -0,0 +1,6 @@
+import qtenv
+
+qtEnv = qtenv.createQtEnvironment()
+Export('qtEnv')
+SConscript('SConscript')
+
diff --git a/workspace/sconstools3/qt6/test/ts_qm/multitarget/sconstest-multitarget.py b/workspace/sconstools3/qt6/test/ts_qm/multitarget/sconstest-multitarget.py
new file mode 100644
index 0000000..2a900fe
--- /dev/null
+++ b/workspace/sconstools3/qt6/test/ts_qm/multitarget/sconstest-multitarget.py
@@ -0,0 +1,59 @@
+#!/usr/bin/env python
+#
+# Copyright (c) 2001-2010,2011,2012 The SCons Foundation
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+
+"""
+Tests that the Ts() and Qm() builders accept and
+process multiple targets correctly.
+"""
+
+import TestSCons
+
+test = TestSCons.TestSCons()
+test.dir_fixture("image")
+test.file_fixture('../../qtenv.py')
+test.file_fixture('../../../__init__.py','site_scons/site_tools/qt5/__init__.py')
+test.run(stderr=None)
+
+test.must_exist(test.workpath('my_en.ts'))
+test.must_exist(test.workpath('my_de.ts'))
+test.must_contain(test.workpath('my_en.ts'),'SCons rocks!')
+test.must_contain(test.workpath('my_de.ts'),'SCons rocks!')
+test.must_exist(test.workpath('my_en.qm'))
+test.must_exist(test.workpath('my_de.qm'))
+
+test.run(options = '-c')
+
+test.must_exist(test.workpath('my_en.ts'))
+test.must_exist(test.workpath('my_de.ts'))
+test.must_not_exist(test.workpath('my_en.qm'))
+test.must_not_exist(test.workpath('my_de.qm'))
+
+test.pass_test()
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/workspace/sconstools3/qt6/test/ts_qm/noclean/image/MyFile.cpp b/workspace/sconstools3/qt6/test/ts_qm/noclean/image/MyFile.cpp
new file mode 100644
index 0000000..09e27c6
--- /dev/null
+++ b/workspace/sconstools3/qt6/test/ts_qm/noclean/image/MyFile.cpp
@@ -0,0 +1,7 @@
+#include "MyFile.h"
+
+aaa::aaa() : my_s(tr("SCons rocks!"))
+{
+  ;
+}
+
diff --git a/workspace/sconstools3/qt6/test/ts_qm/noclean/image/MyFile.h b/workspace/sconstools3/qt6/test/ts_qm/noclean/image/MyFile.h
new file mode 100644
index 0000000..10311dd
--- /dev/null
+++ b/workspace/sconstools3/qt6/test/ts_qm/noclean/image/MyFile.h
@@ -0,0 +1,13 @@
+#include <QObject>
+#include <QString>
+
+class aaa : public QObject
+{
+  Q_OBJECT
+
+public:
+  aaa();
+
+private:
+  QString my_s;
+};
diff --git a/workspace/sconstools3/qt6/test/ts_qm/noclean/image/SConscript b/workspace/sconstools3/qt6/test/ts_qm/noclean/image/SConscript
new file mode 100644
index 0000000..932ec0f
--- /dev/null
+++ b/workspace/sconstools3/qt6/test/ts_qm/noclean/image/SConscript
@@ -0,0 +1,4 @@
+Import("qtEnv")
+
+qtEnv.Ts5('my_en.ts', Glob('*.cpp'))
+qtEnv.Qm5('my_en','my_en')
diff --git a/workspace/sconstools3/qt6/test/ts_qm/noclean/image/SConstruct b/workspace/sconstools3/qt6/test/ts_qm/noclean/image/SConstruct
new file mode 100644
index 0000000..d9d897d
--- /dev/null
+++ b/workspace/sconstools3/qt6/test/ts_qm/noclean/image/SConstruct
@@ -0,0 +1,6 @@
+import qtenv
+
+qtEnv = qtenv.createQtEnvironment()
+Export('qtEnv')
+SConscript('SConscript')
+
diff --git a/workspace/sconstools3/qt6/test/ts_qm/noclean/sconstest-noclean.py b/workspace/sconstools3/qt6/test/ts_qm/noclean/sconstest-noclean.py
new file mode 100644
index 0000000..69320b2
--- /dev/null
+++ b/workspace/sconstools3/qt6/test/ts_qm/noclean/sconstest-noclean.py
@@ -0,0 +1,54 @@
+#!/usr/bin/env python
+#
+# Copyright (c) 2001-2010,2011,2012 The SCons Foundation
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+
+"""
+Tests that .ts files are not removed by default,
+on a 'scons -c'.
+"""
+
+import TestSCons
+
+test = TestSCons.TestSCons()
+test.dir_fixture("image")
+test.file_fixture('../../qtenv.py')
+test.file_fixture('../../../__init__.py','site_scons/site_tools/qt5/__init__.py')
+test.run(stderr=None)
+
+test.must_exist(test.workpath('my_en.ts'))
+test.must_contain(test.workpath('my_en.ts'),'SCons rocks!')
+test.must_exist(test.workpath('my_en.qm'))
+
+test.run(options = '-c')
+
+test.must_exist(test.workpath('my_en.ts'))
+test.must_not_exist(test.workpath('my_en.qm'))
+
+test.pass_test()
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4: