#67401·QGIS

QgsProcessingGuiRegistry returns wrong QgsAbstractProcessingParameterWidgetWrapper class

Author: jakimowbCreated Sep 11, 2026Updated Sep 18, 2026
LabelsPyQGISBug

What is the bug or the crash?

QgsGui.processingGuiRegistry().createParameterWidgetWrapper(...) is used to construct widgets that collect user inputs for a QgsProcessingAlgorithms, e.g. within a ParametersPanel of a AlgorithmDialog.

Implementing and registering a CustomStringParameterWidgetFactory(QgsProcessingParameterWidgetFactoryInterface) can (theoretically) be used to provide a customized widget wrapper. This wrapper creates specialized input widgets, which can be very helpful if algorithms require data inputs beyond those types considered in the standard QGIS API.

In a QgsProcessingAlgorithm, the use of a customized widget wrapper (in QGIS 4 API) is done as in:

python

def initAlgorithm(self, configuration=None):

        param = QgsProcessingParameterString('TEST', 'custom string', optional=False)
        metadata = param.metadata()
        metadata["widget_wrapper"] = {
            "widget_type": (
                CustomStringParameterWidgetFactory.NAME
            )
        }
        param.setMetadata(metadata)
        self.addParameter(param)

The "widget_wrapper" name string is evaluated in QgsProcessingGuiRegistry::createParameterWidgetWrapper to run the factory->createWidgetWrapper( parameter, type ); of the requested CustomStringParameterWidgetFactory.

However, if the factory (QgsProcessingParameterWidgetFactoryInterface) is defined in Python and registered to the QgsProcessingGuiRegistry, QgsProcessingGuiRegistry.createParameterWidgetWrapper(...) only returns the base-class (QgsAbstractProcessingParameterWidgetWrapper) but not the inherited Python instance.

Steps to reproduce the issue

The following python code defines a CustomStringParameterWidgetWrapper. A CustomStringParameterWidgetFactory is registered to the QgsGui.processingGuiRegistry(). test_widget_wrapper_creation tries to create a CustomStringParameterWidgetWrapper instance from (a) the factory (works) and (b) the QgsGui.processingGuiRegistry(), which fails.

File: test_qgsparameterwidgetwrapper.py

python
import unittest

from qgis.PyQt.QtWidgets import QLineEdit
from qgis.core import Qgis, QgsProcessingParameterString
from qgis.gui import QgsProcessingGuiRegistry
from qgis.gui import QgsProcessingParameterWidgetFactoryInterface, QgsAbstractProcessingParameterWidgetWrapper, QgsGui
from qgis.testing import start_app

start_app()


class CustomStringParameterWidgetWrapper(QgsAbstractProcessingParameterWidgetWrapper):

    def __init__(self, *args, **kwds):
        super().__init__(*args, **kwds)
        self._widget = None
        self.setObjectName('CustomWrapper')

    def createWidget(self):
        self._widget = QLineEdit()
        return self._widget

    def setWidgetValue(self, value, context):
        if self._widget is not None and value is not None:
            self._widget.setText(str(value))

    def widgetValue(self, context):
        if self._widget is not None:
            return self._widget.text()
        return None


class CustomStringParameterWidgetFactory(QgsProcessingParameterWidgetFactoryInterface):
    NAME = 'MyParameterWidget'

    CREATED_WIDGET_WRAPPERS = 0

    def createWidgetWrapper(self, *args, **kwds):
        w = CustomStringParameterWidgetWrapper(*args, **kwds)
        CustomStringParameterWidgetFactory.CREATED_WIDGET_WRAPPERS += 1
        return w

    def clone(self):
        return CustomStringParameterWidgetFactory()

    def parameterType(self):
        return self.NAME


class TestCustomParameterWidget(unittest.TestCase):

    @classmethod
    def setUpClass(cls):
        cls._factory = CustomStringParameterWidgetFactory()
        QgsGui.processingGuiRegistry().addParameterWidgetFactory(cls._factory)

    @classmethod
    def tearDownClass(cls):
        QgsGui.processingGuiRegistry().removeParameterWidgetFactory(cls._factory)

    def test_widget_wrapper_creation(self):
        """Test creating widget wrapper for custom parameter."""
        # Create parameter

        param = QgsProcessingParameterString('TEST', 'custom string', optional=False)

        metadata = param.metadata()
        metadata["widget_wrapper"] = {
            "widget_type": (
                CustomStringParameterWidgetFactory.NAME
            )
        }
        param.setMetadata(metadata)
        wrapper_type = Qgis.ProcessingMode.Standard

        # use CustomStringParameterWidgetFactory directly
        self.assertEqual(CustomStringParameterWidgetFactory.CREATED_WIDGET_WRAPPERS, 0)
        wrapper1 = self._factory.createWidgetWrapper(param, wrapper_type)
        self.assertEqual(CustomStringParameterWidgetFactory.CREATED_WIDGET_WRAPPERS, 1)
        self.assertEqual(wrapper1.objectName(), "CustomWrapper")
        self.assertIsInstance(wrapper1, QgsAbstractProcessingParameterWidgetWrapper)
        self.assertIsInstance(wrapper1, CustomStringParameterWidgetWrapper)

        # use the CustomStringParameterWidgetFactory, where we have registered the CustomStringParameterWidgetFactory
        # to
        reg: QgsProcessingGuiRegistry = QgsGui.processingGuiRegistry()

        wrapper2 = reg.createParameterWidgetWrapper(param, wrapper_type)
        self.assertEqual(CustomStringParameterWidgetFactory.CREATED_WIDGET_WRAPPERS, 2)
        self.assertEqual(wrapper2.objectName(), "CustomWrapper")
        self.assertIsInstance(wrapper2, QgsAbstractProcessingParameterWidgetWrapper)

        # this fails, because QgsProcessingGuiRegistry returns the base class
        # QgsAbstractProcessingParameterWidgetWrapper only
        self.assertIsInstance(wrapper2, CustomStringParameterWidgetWrapper)


if __name__ == '__main__':
    unittest.main()

Run this test, e.g. using the official QGIS docker images:

bash
#!/bin/bash
SCRIPT_DIR="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)"

docker run --rm \
  --network host \
  --user "$(id -u):$(id -g)" \
  --workdir /workspace \
  -e "PYTHONPATH=/usr/share/qgis/python:/usr/share/qgis/python/plugins" \
  -e QT_QPA_PLATFORM=offscreen \
  -v "${SCRIPT_DIR}:/workspace" \
  -v "${HOME}:${HOME}" \
  -v /tmp:/tmp \
  qgis/qgis:stable \
  python3 test_qgsparameterwidgetwrapper.py

Versions

QGIS: 4.3.0-Master d3b565c628d (Linux, qgis/qgis:latest)

Supported QGIS version

  • I'm running a supported QGIS version according to the roadmap.

New profile

No Duplicate of the Issue

  • I have verified that there are no existing issues raised related to my problem.

Additional context

This issue is likely related to #48816, which used an old (< QGIS 4.0) way to define custom widget wrapper.