diff --git a/python/gui/auto_generated/processing/qgsprocessingguiregistry.sip.in b/python/gui/auto_generated/processing/qgsprocessingguiregistry.sip.in index 8d27f6b2316..1f444da2948 100644 --- a/python/gui/auto_generated/processing/qgsprocessingguiregistry.sip.in +++ b/python/gui/auto_generated/processing/qgsprocessingguiregistry.sip.in @@ -10,13 +10,15 @@ - class QgsProcessingGuiRegistry { %Docstring The QgsProcessingGuiRegistry is a home for widgets for processing configuration widgets. +QgsProcessingGuiRegistry is not usually directly created, but rather accessed through +:py:func:`QgsGui.processingGuiRegistry()` + .. versionadded:: 3.2 %End @@ -55,6 +57,41 @@ next to parameter widgets. Most algorithms do not have a configuration widget and in this case, None will be returned. .. versionadded:: 3.2 +%End + + bool addParameterWidgetFactory( QgsProcessingParameterWidgetFactoryInterface *factory /Transfer/ ); +%Docstring +Adds a parameter widget ``factory`` to the registry. Ownership of ``factory`` is transferred to the registry. + +Returns true if the factory was successfully added, or false if the factory could not be added. Each +factory must return a unique value for QgsProcessingParameterWidgetFactoryInterface.parameterType(), +and attempting to add a new factory with a duplicate type will result in failure. + +.. seealso:: :py:func:`removeParameterWidgetFactory` + +.. seealso:: :py:func:`createParameterWidgetWrapper` + +.. versionadded:: 3.4 +%End + + void removeParameterWidgetFactory( QgsProcessingParameterWidgetFactoryInterface *factory ); +%Docstring +Removes a parameter widget ``factory`` from the registry. The factory will be deleted. + +.. seealso:: :py:func:`addParameterWidgetFactory` + +.. versionadded:: 3.4 +%End + + QgsAbstractProcessingParameterWidgetWrapper *createParameterWidgetWrapper( const QgsProcessingParameterDefinition *parameter, QgsAbstractProcessingParameterWidgetWrapper::WidgetType type ) /Factory/; +%Docstring +Creates a new parameter widget wrapper for the given ``parameter``. The ``type`` argument +dictates the type of dialog the wrapper should be created for. The caller takes ownership +of the returned wrapper. + +If no factory is registered which handles the given ``parameter``, a None will be returned. + +.. seealso:: :py:func:`addParameterWidgetFactory` %End }; diff --git a/python/gui/auto_generated/processing/qgsprocessingwidgetwrapper.sip.in b/python/gui/auto_generated/processing/qgsprocessingwidgetwrapper.sip.in new file mode 100644 index 00000000000..4c52319750a --- /dev/null +++ b/python/gui/auto_generated/processing/qgsprocessingwidgetwrapper.sip.in @@ -0,0 +1,194 @@ +/************************************************************************ + * This file has been generated automatically from * + * * + * src/gui/processing/qgsprocessingwidgetwrapper.h * + * * + * Do not edit manually ! Edit header and run scripts/sipify.pl again * + ************************************************************************/ + + + + + + +class QgsAbstractProcessingParameterWidgetWrapper : QObject +{ +%Docstring + +A widget wrapper for Processing parameter value widgets. + +Widget wrappers are used to create widgets for individual Processing parameters, and +handle retrieving and setting the current value for those parameters. + +Widget wrappers can be created for different dialog types, allowing different +appearance and behavior of widgets depending on whether they are being created for +use in a standard algorithm dialog, a batch processing dialog, or a modeler dialog. + +Individual widget wrappers are not usually created directly, instead they are +constructed through the central registry, via calls to +QgsGui.processingGuiRegistry()->createParameterWidgetWrapper(). + +.. versionadded:: 3.4 +%End + +%TypeHeaderCode +#include "qgsprocessingwidgetwrapper.h" +%End + public: + + enum WidgetType + { + Standard, + Batch, + Modeler, + }; + + QgsAbstractProcessingParameterWidgetWrapper( const QgsProcessingParameterDefinition *parameter = 0, + WidgetType type = Standard, QObject *parent /TransferThis/ = 0 ); +%Docstring +Constructor for QgsAbstractProcessingParameterWidgetWrapper, for the specified +``parameter`` definition and dialog ``type``. +%End + + WidgetType type() const; +%Docstring +Returns the dialog type for which widgets and labels will be created by this wrapper. +%End + + QWidget *createWrappedWidget( const QgsProcessingContext &context ) /Factory/; +%Docstring +Creates and return a new wrapped widget which allows customization of the parameter's value. + +The caller takes ownership of the returned widget. The wrapped widget can be retrieved at a later +stage by calling wrappedWidget(). + +The supplied ``context`` is used while initializing the widget to the parameter's default value. + +.. seealso:: :py:func:`createWrappedLabel` +%End + + QLabel *createWrappedLabel() /Factory/; +%Docstring +Creates and returns a new label to accompany widgets created by the wrapper. + +The caller takes ownership of the returned label. Some parameter type and dialog type +combinations will return None for this method. If None is returned, then no +label should be shown for the parameter's widget (i.e. the label is embedded inside the +widget itself). + +The wrapped label can be retrieved at a later stage by calling wrappedLabel(). + +.. seealso:: :py:func:`createWrappedWidget` +%End + + QWidget *wrappedWidget(); +%Docstring +Returns the current wrapped widget, if any. + +.. seealso:: :py:func:`createWrappedWidget` +%End + + QLabel *wrappedLabel(); +%Docstring +Returns the current wrapped label, if any. + +.. seealso:: :py:func:`createWrappedLabel` +%End + + const QgsProcessingParameterDefinition *parameterDefinition() const; +%Docstring +Returns the parameter definition associated with this wrapper. +%End + + virtual void setWidgetValue( const QVariant &value, const QgsProcessingContext &context ) = 0; +%Docstring +Sets the current ``value`` for the parameter. + +The ``context`` argument is used to specify the wider Processing context which the +current value is associated with. + +.. seealso:: :py:func:`value` +%End + + virtual QVariant value() const = 0; +%Docstring +Returns the current value of the parameter. + +.. seealso:: :py:func:`setWidgetValue` +%End + + virtual void postInitialize( const QList< QgsAbstractProcessingParameterWidgetWrapper * > &wrappers ); +%Docstring +Called after all wrappers have been created within a particular dialog or context, +allowing the wrapper to connect to the wrappers of other, related parameters. +%End + + protected: + + virtual QWidget *createWidget() = 0 /Factory/; +%Docstring +Creates a new widget which allows customization of the parameter's value. + +The caller takes ownership of the returned widget. + +.. seealso:: :py:func:`createLabel` +%End + + virtual QLabel *createLabel() /Factory/; +%Docstring +Creates a new label to accompany widgets created by the wrapper. + +The caller takes ownership of the returned label. Some parameter type and dialog type +combinations will return None for this method. If None is returned, then no +label should be shown for the parameter's widget (i.e. the label is embedded inside the +widget itself). + +.. seealso:: :py:func:`createWidget` +%End + +}; + + +class QgsProcessingParameterWidgetFactoryInterface +{ +%Docstring + +An interface for Processing widget wrapper factories. + +Widget wrapper factories allow creation of QgsAbstractProcessingParameterWidgetWrapper objects. +They are centrally managed by :py:class:`QgsProcessingGuiRegistry`. Usually, individual factories +are not directly utilized, rather the QgsGui.processingGuiRegistry()->createParameterWidgetWrapper() +method is used to create widget wrappers. + +.. versionadded:: 3.4 +%End + +%TypeHeaderCode +#include "qgsprocessingwidgetwrapper.h" +%End + public: + + virtual ~QgsProcessingParameterWidgetFactoryInterface(); + + virtual QString parameterType() const = 0; +%Docstring +Returns the type string for the parameter type the factory is associated with. +%End + + virtual QgsAbstractProcessingParameterWidgetWrapper *createWidgetWrapper( const QgsProcessingParameterDefinition *parameter, + QgsAbstractProcessingParameterWidgetWrapper::WidgetType type ) = 0 /Factory/; +%Docstring +Creates a new widget wrapper for the specified ``parameter`` definition. + +The ``type`` argument indicates the dialog type to create a wrapper for. +%End + +}; + +/************************************************************************ + * This file has been generated automatically from * + * * + * src/gui/processing/qgsprocessingwidgetwrapper.h * + * * + * Do not edit manually ! Edit header and run scripts/sipify.pl again * + ************************************************************************/ diff --git a/python/gui/gui_auto.sip b/python/gui/gui_auto.sip index 321bd6351eb..ebfd427d61c 100644 --- a/python/gui/gui_auto.sip +++ b/python/gui/gui_auto.sip @@ -319,4 +319,5 @@ %Include auto_generated/processing/qgsprocessingrecentalgorithmlog.sip %Include auto_generated/processing/qgsprocessingtoolboxmodel.sip %Include auto_generated/processing/qgsprocessingtoolboxtreeview.sip +%Include auto_generated/processing/qgsprocessingwidgetwrapper.sip %Include auto_generated/qgsadvanceddigitizingcanvasitem.sip diff --git a/python/plugins/processing/algs/gdal/ui/RasterOptionsWidget.py b/python/plugins/processing/algs/gdal/ui/RasterOptionsWidget.py index 87ce4bc23a8..add72db276c 100644 --- a/python/plugins/processing/algs/gdal/ui/RasterOptionsWidget.py +++ b/python/plugins/processing/algs/gdal/ui/RasterOptionsWidget.py @@ -43,12 +43,12 @@ class RasterOptionsWidgetWrapper(WidgetWrapper): options = [(self.dialog.resolveValueDescription(s), s) for s in strings] for desc, val in options: widget.addItem(desc, val) - widget.setEditText(self.param.defaultValue() or '') + widget.setEditText(self.parameterDefinition().defaultValue() or '') return widget elif self.dialogType == DIALOG_BATCH: widget = QLineEdit() - if self.param.defaultValue(): - widget.setText(self.param.defaultValue()) + if self.parameterDefinition().defaultValue(): + widget.setText(self.parameterDefinition().defaultValue()) return widget else: return QgsRasterFormatSaveOptionsWidget() diff --git a/python/plugins/processing/algs/qgis/ui/FieldsMappingPanel.py b/python/plugins/processing/algs/qgis/ui/FieldsMappingPanel.py index 591424690ce..ff94fb838dc 100644 --- a/python/plugins/processing/algs/qgis/ui/FieldsMappingPanel.py +++ b/python/plugins/processing/algs/qgis/ui/FieldsMappingPanel.py @@ -513,7 +513,7 @@ class FieldsMappingWidgetWrapper(WidgetWrapper): def postInitialize(self, wrappers): for wrapper in wrappers: - if wrapper.param.name() == self.param.parentLayerParameter(): + if wrapper.param.name() == self.parameterDefinition().parentLayerParameter(): if wrapper.value(): self.setLayer(wrapper.value()) wrapper.widgetValueHasChanged.connect(self.parentLayerChanged) diff --git a/python/plugins/processing/algs/qgis/ui/HeatmapWidgets.py b/python/plugins/processing/algs/qgis/ui/HeatmapWidgets.py index 48087f4c873..cf56a3e1259 100644 --- a/python/plugins/processing/algs/qgis/ui/HeatmapWidgets.py +++ b/python/plugins/processing/algs/qgis/ui/HeatmapWidgets.py @@ -181,13 +181,13 @@ class HeatmapPixelSizeWidgetWrapper(WidgetWrapper): return for wrapper in wrappers: - if wrapper.param.name() == self.param.parent_layer: + if wrapper.parameterDefinition().name() == self.param.parent_layer: self.setSource(wrapper.value()) wrapper.widgetValueHasChanged.connect(self.parentLayerChanged) - elif wrapper.param.name() == self.param.radius_param: + elif wrapper.parameterDefinition().name() == self.param.radius_param: self.setRadius(wrapper.value()) wrapper.widgetValueHasChanged.connect(self.radiusChanged) - elif wrapper.param.name() == self.param.radius_field_param: + elif wrapper.parameterDefinition().name() == self.param.radius_field_param: self.setSource(wrapper.value()) wrapper.widgetValueHasChanged.connect(self.radiusFieldChanged) diff --git a/python/plugins/processing/algs/qgis/ui/RasterCalculatorWidgets.py b/python/plugins/processing/algs/qgis/ui/RasterCalculatorWidgets.py index 0cec0b9f9d4..07c2211ee46 100644 --- a/python/plugins/processing/algs/qgis/ui/RasterCalculatorWidgets.py +++ b/python/plugins/processing/algs/qgis/ui/RasterCalculatorWidgets.py @@ -223,30 +223,30 @@ class ExpressionWidgetWrapper(WidgetWrapper): for lyr in layers: for n in range(lyr.bandCount()): options[lyr.name()] = '{:s}@{:d}'.format(lyr.name(), n + 1) - self.widget.setList(options) + self.widget().setList(options) def setValue(self, value): if self.dialogType == DIALOG_STANDARD: pass # TODO elif self.dialogType == DIALOG_BATCH: - return self.widget.setText(value) + return self.widget().setText(value) else: - self.widget.setValue(value) + self.widget().setValue(value) def value(self): if self.dialogType in DIALOG_STANDARD: - return self.widget.value() + return self.widget().value() elif self.dialogType == DIALOG_BATCH: - return self.widget.text() + return self.widget().text() else: - return self.widget.value() + return self.widget().value() class LayersListWidgetWrapper(WidgetWrapper): def createWidget(self): if self.dialogType == DIALOG_BATCH: - widget = BatchInputSelectionPanel(self.param, self.row, self.col, self.dialog) + widget = BatchInputSelectionPanel(self.parameterDefinition(), self.row, self.col, self.dialog) widget.valueChanged.connect(lambda: self.widgetValueHasChanged.emit(self)) return widget else: @@ -272,7 +272,7 @@ class LayersListWidgetWrapper(WidgetWrapper): return self.widget.getText() else: options = self._getOptions() - values = [options[i] for i in self.widget.selectedoptions] - if len(values) == 0 and not self.param.flags() & QgsProcessingParameterDefinition.FlagOptional: + values = [options[i] for i in self.widget().selectedoptions] + if len(values) == 0 and not self.parameterDefinition().flags() & QgsProcessingParameterDefinition.FlagOptional: raise InvalidParameterValue() return values diff --git a/python/plugins/processing/gui/AlgorithmDialog.py b/python/plugins/processing/gui/AlgorithmDialog.py index dbb9a84d379..512494fbafb 100644 --- a/python/plugins/processing/gui/AlgorithmDialog.py +++ b/python/plugins/processing/gui/AlgorithmDialog.py @@ -98,14 +98,23 @@ class AlgorithmDialog(QgsProcessingAlgorithmDialogBase): if param.flags() & QgsProcessingParameterDefinition.FlagHidden: continue if not param.isDestination(): - wrapper = self.mainWidget().wrappers[param.name()] - value = None - if wrapper.widget is not None: - value = wrapper.value() - parameters[param.name()] = value + try: + wrapper = self.mainWidget().wrappers[param.name()] + except KeyError: + continue - if not param.checkValueIsAcceptable(value): - raise AlgorithmDialogBase.InvalidParameterValue(param, wrapper.widget) + try: + widget = wrapper.wrappedWidget() + except AttributeError: + widget = wrapper.widget + if widget is None: + continue + + value = wrapper.value() + parameters[param.name()] = value + + if not param.checkValueIsAcceptable(value): + raise AlgorithmDialogBase.InvalidParameterValue(param, widget) else: dest_project = None if not param.flags() & QgsProcessingParameterDefinition.FlagHidden and \ diff --git a/python/plugins/processing/gui/BatchPanel.py b/python/plugins/processing/gui/BatchPanel.py index 55986baf44b..acba286cedf 100644 --- a/python/plugins/processing/gui/BatchPanel.py +++ b/python/plugins/processing/gui/BatchPanel.py @@ -36,7 +36,7 @@ from qgis.core import (Qgis, QgsApplication, QgsSettings, QgsProcessingParameterDefinition) -from qgis.gui import QgsMessageBar +from qgis.gui import QgsAbstractProcessingParameterWidgetWrapper from processing.gui.wrappers import WidgetWrapperFactory from processing.gui.BatchOutputSelectionPanel import BatchOutputSelectionPanel @@ -232,14 +232,23 @@ class BatchPanel(BASE, WIDGET): with open(filename, 'w') as f: json.dump(toSave, f) - def setCellWrapper(self, row, column, wrapper): + def setCellWrapper(self, row, column, wrapper, context): self.wrappers[row][column] = wrapper - self.tblParameters.setCellWidget(row, column, wrapper.widget) + + is_cpp_wrapper = issubclass(wrapper.__class__, QgsAbstractProcessingParameterWidgetWrapper) + if is_cpp_wrapper: + widget = wrapper.createWrappedWidget(context) + else: + widget = wrapper.widget + + self.tblParameters.setCellWidget(row, column, widget) def addRow(self): self.wrappers.append([None] * self.tblParameters.columnCount()) self.tblParameters.setRowCount(self.tblParameters.rowCount() + 1) + context = dataobjects.createContext() + wrappers = {} row = self.tblParameters.rowCount() - 1 column = 0 @@ -249,7 +258,7 @@ class BatchPanel(BASE, WIDGET): wrapper = WidgetWrapperFactory.create_wrapper(param, self.parent, row, column) wrappers[param.name()] = wrapper - self.setCellWrapper(row, column, wrapper) + self.setCellWrapper(row, column, wrapper, context) column += 1 for out in self.alg.destinationParameterDefinitions(): diff --git a/python/plugins/processing/gui/ParametersPanel.py b/python/plugins/processing/gui/ParametersPanel.py index ac5f9045f62..05d18487396 100644 --- a/python/plugins/processing/gui/ParametersPanel.py +++ b/python/plugins/processing/gui/ParametersPanel.py @@ -44,6 +44,9 @@ from qgis.core import (QgsProcessingParameterDefinition, QgsProcessingParameterFeatureSink, QgsProcessingParameterVectorDestination, QgsProject) +from qgis.gui import (QgsGui, + QgsAbstractProcessingParameterWidgetWrapper) + from qgis.PyQt import uic from qgis.PyQt.QtCore import QCoreApplication, Qt from qgis.PyQt.QtWidgets import (QWidget, QHBoxLayout, QToolButton, @@ -52,7 +55,7 @@ from qgis.PyQt.QtGui import QIcon from processing.gui.DestinationSelectionPanel import DestinationSelectionPanel from processing.gui.wrappers import WidgetWrapperFactory - +from processing.tools.dataobjects import createContext pluginPath = os.path.split(os.path.dirname(__file__))[0]\ @@ -91,7 +94,10 @@ class ParametersPanel(BASE, WIDGET): def layerRegistryChanged(self, layers): for wrapper in list(self.wrappers.values()): - wrapper.refresh() + try: + wrapper.refresh() + except AttributeError: + pass def formatParameterTooltip(self, parameter): return '
{}
{}
'.format( @@ -100,6 +106,8 @@ class ParametersPanel(BASE, WIDGET): ) def initWidgets(self): + context = createContext() + # If there are advanced parameters — show corresponding groupbox for param in self.alg.parameterDefinitions(): if param.flags() & QgsProcessingParameterDefinition.FlagAdvanced: @@ -115,9 +123,16 @@ class ParametersPanel(BASE, WIDGET): else: wrapper = WidgetWrapperFactory.create_wrapper(param, self.parent) self.wrappers[param.name()] = wrapper - widget = wrapper.widget + is_cpp_wrapper = issubclass(wrapper.__class__, QgsAbstractProcessingParameterWidgetWrapper) + if is_cpp_wrapper: + widget = wrapper.createWrappedWidget(context) + else: + widget = wrapper.widget if widget is not None: + if not is_cpp_wrapper: + widget.setToolTip(param.toolTip()) + if isinstance(param, QgsProcessingParameterFeatureSource): layout = QHBoxLayout() layout.setSpacing(6) @@ -136,15 +151,19 @@ class ParametersPanel(BASE, WIDGET): widget = QWidget() widget.setLayout(layout) - widget.setToolTip(param.toolTip()) + label = None + if is_cpp_wrapper: + label = wrapper.createWrappedLabel() + else: + label = wrapper.label - if wrapper.label is not None: + if label is not None: if param.flags() & QgsProcessingParameterDefinition.FlagAdvanced: - self.layoutAdvanced.addWidget(wrapper.label) + self.layoutAdvanced.addWidget(label) else: self.layoutMain.insertWidget( - self.layoutMain.count() - 2, wrapper.label) - else: + self.layoutMain.count() - 2, label) + elif not is_cpp_wrapper: desc = param.description() if isinstance(param, QgsProcessingParameterExtent): desc += self.tr(' (xmin, xmax, ymin, ymax)') @@ -188,6 +207,7 @@ class ParametersPanel(BASE, WIDGET): wrapper.postInitialize(list(self.wrappers.values())) def setParameters(self, parameters): + context = createContext() for param in self.alg.parameterDefinitions(): if param.flags() & QgsProcessingParameterDefinition.FlagHidden: continue @@ -196,8 +216,10 @@ class ParametersPanel(BASE, WIDGET): continue if not param.isDestination(): + value = parameters[param.name()] + wrapper = self.wrappers[param.name()] - wrapper.setValue(parameters[param.name()]) + wrapper.setWidgetValue(value, context) else: dest_widget = self.outputWidgets[param.name()] dest_widget.setValue(parameters[param.name()]) diff --git a/python/plugins/processing/gui/wrappers.py b/python/plugins/processing/gui/wrappers.py index 65164dd42f3..8e7be47f1b4 100755 --- a/python/plugins/processing/gui/wrappers.py +++ b/python/plugins/processing/gui/wrappers.py @@ -94,6 +94,7 @@ from qgis.PyQt.QtWidgets import ( QWidget, ) from qgis.gui import ( + QgsGui, QgsExpressionLineEdit, QgsExpressionBuilderDialog, QgsFieldComboBox, @@ -102,6 +103,7 @@ from qgis.gui import ( QgsMapLayerComboBox, QgsProjectionSelectionWidget, QgsRasterBandComboBox, + QgsAbstractProcessingParameterWidgetWrapper ) from qgis.PyQt.QtCore import pyqtSignal, QObject, QVariant, Qt from qgis.utils import iface @@ -122,9 +124,9 @@ from processing.gui.ParameterGuiUtils import getFileFilter from processing.tools import dataobjects -DIALOG_STANDARD = 'standard' -DIALOG_BATCH = 'batch' -DIALOG_MODELER = 'modeler' +DIALOG_STANDARD = QgsAbstractProcessingParameterWidgetWrapper.Standard +DIALOG_BATCH = QgsAbstractProcessingParameterWidgetWrapper.Batch +DIALOG_MODELER = QgsAbstractProcessingParameterWidgetWrapper.Modeler class InvalidParameterValue(Exception): @@ -144,18 +146,19 @@ def getExtendedLayerName(layer): return layer.name() -class WidgetWrapper(QObject): +class WidgetWrapper(QgsAbstractProcessingParameterWidgetWrapper): widgetValueHasChanged = pyqtSignal(object) NOT_SET_OPTION = '~~~~!!!!NOT SET!!!!~~~~~~~' def __init__(self, param, dialog, row=0, col=0, **kwargs): - QObject.__init__(self) + self.dialogType = dialogTypes.get(dialog.__class__.__name__, QgsAbstractProcessingParameterWidgetWrapper.Standard) + super().__init__(param, self.dialogType) self.param = param self.dialog = dialog self.row = row self.col = col - self.dialogType = dialogTypes.get(dialog.__class__.__name__, DIALOG_STANDARD) + self.widget = self.createWidget(**kwargs) self.label = self.createLabel() if param.defaultValue() is not None: @@ -183,21 +186,24 @@ class WidgetWrapper(QObject): def createLabel(self): if self.dialogType == DIALOG_BATCH: return None - desc = self.param.description() - if isinstance(self.param, QgsProcessingParameterExtent): + desc = self.parameterDefinition().description() + if isinstance(self.parameterDefinition(), QgsProcessingParameterExtent): desc += self.tr(' (xmin, xmax, ymin, ymax)') - if isinstance(self.param, QgsProcessingParameterPoint): + if isinstance(self.parameterDefinition(), QgsProcessingParameterPoint): desc += self.tr(' (x, y)') - if self.param.flags() & QgsProcessingParameterDefinition.FlagOptional: + if self.parameterDefinition().flags() & QgsProcessingParameterDefinition.FlagOptional: desc += self.tr(' [optional]') label = QLabel(desc) - label.setToolTip(self.param.name()) + label.setToolTip(self.parameterDefinition().name()) return label def setValue(self, value): pass + def setWidgetValue(self, value, context): + self.setValue(value) + def setComboValue(self, value, combobox=None): if combobox is None: combobox = self.widget @@ -219,12 +225,6 @@ class WidgetWrapper(QObject): else: combobox.setCurrentIndex(0) - def value(self): - pass - - def postInitialize(self, wrappers): - pass - def refresh(self): pass @@ -242,7 +242,7 @@ class WidgetWrapper(QObject): # TODO: should use selectedFilter argument for default file format filename, selected_filter = QFileDialog.getOpenFileName(self.widget, self.tr('Select File'), - path, getFileFilter(self.param)) + path, getFileFilter(self.parameterDefinition())) if filename: settings.setValue('/Processing/LastInputPath', os.path.dirname(str(filename))) @@ -331,19 +331,19 @@ class CrsWidgetWrapper(WidgetWrapper): QgsProcessingOutputMapLayer]) for l in layers: self.combo.addItem("Crs of layer " + self.dialog.resolveValueDescription(l), l) - if self.param.defaultValue(): - self.combo.setEditText(self.param.defaultValue()) + if self.parameterDefinition().defaultValue(): + self.combo.setEditText(self.parameterDefinition().defaultValue()) return widget else: widget = QgsProjectionSelectionWidget() - if self.param.flags() & QgsProcessingParameterDefinition.FlagOptional: + if self.parameterDefinition().flags() & QgsProcessingParameterDefinition.FlagOptional: widget.setOptionVisible(QgsProjectionSelectionWidget.CrsNotSet, True) - if self.param.defaultValue(): - if self.param.defaultValue() == 'ProjectCrs': + if self.parameterDefinition().defaultValue(): + if self.parameterDefinition().defaultValue() == 'ProjectCrs': crs = QgsProject.instance().crs() else: - crs = QgsCoordinateReferenceSystem(self.param.defaultValue()) + crs = QgsCoordinateReferenceSystem(self.parameterDefinition().defaultValue()) widget.setCrs(crs) else: widget.setOptionVisible(QgsProjectionSelectionWidget.CrsNotSet, True) @@ -387,12 +387,12 @@ class ExtentWidgetWrapper(WidgetWrapper): def createWidget(self): if self.dialogType in (DIALOG_STANDARD, DIALOG_BATCH): - return ExtentSelectionPanel(self.dialog, self.param) + return ExtentSelectionPanel(self.dialog, self.parameterDefinition()) else: widget = QComboBox() widget.setEditable(True) extents = self.dialog.getAvailableValuesOfType(QgsProcessingParameterExtent, (QgsProcessingOutputString)) - if self.param.flags() & QgsProcessingParameterDefinition.FlagOptional: + if self.parameterDefinition().flags() & QgsProcessingParameterDefinition.FlagOptional: widget.addItem(self.USE_MIN_COVERING_EXTENT, None) layers = self.dialog.getAvailableValuesOfType([QgsProcessingParameterFeatureSource, QgsProcessingParameterRasterLayer, @@ -404,8 +404,8 @@ class ExtentWidgetWrapper(WidgetWrapper): widget.addItem(self.dialog.resolveValueDescription(ex), ex) for l in layers: widget.addItem("Extent of " + self.dialog.resolveValueDescription(l), l) - if not self.param.defaultValue(): - widget.setEditText(self.param.defaultValue()) + if not self.parameterDefinition().defaultValue(): + widget.setEditText(self.parameterDefinition().defaultValue()) return widget def setValue(self, value): @@ -433,7 +433,7 @@ class ExtentWidgetWrapper(WidgetWrapper): float(token) except: raise InvalidParameterValue() - elif self.param.flags() & QgsProcessingParameterDefinition.FlagOptional: + elif self.parameterDefinition().flags() & QgsProcessingParameterDefinition.FlagOptional: s = None else: raise InvalidParameterValue() @@ -446,14 +446,14 @@ class PointWidgetWrapper(WidgetWrapper): def createWidget(self): if self.dialogType in (DIALOG_STANDARD, DIALOG_BATCH): - return PointSelectionPanel(self.dialog, self.param.defaultValue()) + return PointSelectionPanel(self.dialog, self.parameterDefinition().defaultValue()) else: item = QComboBox() item.setEditable(True) points = self.dialog.getAvailableValuesOfType((QgsProcessingParameterPoint, QgsProcessingParameterString), (QgsProcessingOutputString)) for p in points: item.addItem(self.dialog.resolveValueDescription(p), p) - item.setEditText(str(self.param.defaultValue())) + item.setEditText(str(self.parameterDefinition().defaultValue())) return item def setValue(self, value): @@ -481,7 +481,7 @@ class PointWidgetWrapper(WidgetWrapper): float(token) except: raise InvalidParameterValue() - elif self.param.flags() & QgsProcessingParameterDefinition.FlagOptional: + elif self.parameterDefinition().flags() & QgsProcessingParameterDefinition.FlagOptional: s = None else: raise InvalidParameterValue() @@ -494,15 +494,15 @@ class FileWidgetWrapper(WidgetWrapper): def createWidget(self): if self.dialogType in (DIALOG_STANDARD, DIALOG_BATCH): - return FileSelectionPanel(self.param.behavior() == QgsProcessingParameterFile.Folder, - self.param.extension()) + return FileSelectionPanel(self.parameterDefinition().behavior() == QgsProcessingParameterFile.Folder, + self.parameterDefinition().extension()) else: self.combo = QComboBox() self.combo.setEditable(True) files = self.dialog.getAvailableValuesOfType(QgsProcessingParameterFile, (QgsProcessingOutputRasterLayer, QgsProcessingOutputVectorLayer, QgsProcessingOutputMapLayer, QgsProcessingOutputFile, QgsProcessingOutputString)) for f in files: self.combo.addItem(self.dialog.resolveValueDescription(f), f) - if self.param.flags() & QgsProcessingParameterDefinition.FlagOptional: + if self.parameterDefinition().flags() & QgsProcessingParameterDefinition.FlagOptional: self.combo.setEditText("") widget = QWidget() layout = QHBoxLayout() @@ -527,9 +527,9 @@ class FileWidgetWrapper(WidgetWrapper): else: path = '' - if self.param.extension(): + if self.parameterDefinition().extension(): filter = self.tr('{} files').format( - self.param.extension().upper()) + ' (*.' + self.param.extension() + self.tr( + self.parameterDefinition().extension().upper()) + ' (*.' + self.parameterDefinition().extension() + self.tr( ');;All files (*.*)') else: filter = self.tr('All files (*.*)') @@ -560,7 +560,7 @@ class FixedTableWidgetWrapper(WidgetWrapper): def createWidget(self): if self.dialogType in (DIALOG_STANDARD, DIALOG_BATCH): - return FixedTablePanel(self.param) + return FixedTablePanel(self.parameterDefinition()) else: self.combobox = QComboBox() values = self.dialog.getAvailableValuesOfType(QgsProcessingParameterMatrix) @@ -584,14 +584,14 @@ class FixedTableWidgetWrapper(WidgetWrapper): class MultipleLayerWidgetWrapper(WidgetWrapper): def _getOptions(self): - if self.param.layerType() == QgsProcessing.TypeVectorAnyGeometry: + if self.parameterDefinition().layerType() == QgsProcessing.TypeVectorAnyGeometry: options = self.dialog.getAvailableValuesOfType((QgsProcessingParameterFeatureSource, QgsProcessingParameterVectorLayer, QgsProcessingParameterMultipleLayers), [QgsProcessingOutputVectorLayer, QgsProcessingOutputMapLayer, QgsProcessingOutputMultipleLayers]) - elif self.param.layerType() == QgsProcessing.TypeVector: + elif self.parameterDefinition().layerType() == QgsProcessing.TypeVector: options = self.dialog.getAvailableValuesOfType((QgsProcessingParameterFeatureSource, QgsProcessingParameterVectorLayer, QgsProcessingParameterMultipleLayers), @@ -599,7 +599,7 @@ class MultipleLayerWidgetWrapper(WidgetWrapper): QgsProcessingOutputMapLayer, QgsProcessingOutputMultipleLayers], [QgsProcessing.TypeVector]) - elif self.param.layerType() == QgsProcessing.TypeVectorPoint: + elif self.parameterDefinition().layerType() == QgsProcessing.TypeVectorPoint: options = self.dialog.getAvailableValuesOfType((QgsProcessingParameterFeatureSource, QgsProcessingParameterVectorLayer, QgsProcessingParameterMultipleLayers), @@ -608,7 +608,7 @@ class MultipleLayerWidgetWrapper(WidgetWrapper): QgsProcessingOutputMultipleLayers], [QgsProcessing.TypeVectorPoint, QgsProcessing.TypeVectorAnyGeometry]) - elif self.param.layerType() == QgsProcessing.TypeVectorLine: + elif self.parameterDefinition().layerType() == QgsProcessing.TypeVectorLine: options = self.dialog.getAvailableValuesOfType((QgsProcessingParameterFeatureSource, QgsProcessingParameterVectorLayer, QgsProcessingParameterMultipleLayers), @@ -617,7 +617,7 @@ class MultipleLayerWidgetWrapper(WidgetWrapper): QgsProcessingOutputMultipleLayers], [QgsProcessing.TypeVectorLine, QgsProcessing.TypeVectorAnyGeometry]) - elif self.param.layerType() == QgsProcessing.TypeVectorPolygon: + elif self.parameterDefinition().layerType() == QgsProcessing.TypeVectorPolygon: options = self.dialog.getAvailableValuesOfType((QgsProcessingParameterFeatureSource, QgsProcessingParameterVectorLayer, QgsProcessingParameterMultipleLayers), @@ -626,13 +626,13 @@ class MultipleLayerWidgetWrapper(WidgetWrapper): QgsProcessingOutputMultipleLayers], [QgsProcessing.TypeVectorPolygon, QgsProcessing.TypeVectorAnyGeometry]) - elif self.param.layerType() == QgsProcessing.TypeRaster: + elif self.parameterDefinition().layerType() == QgsProcessing.TypeRaster: options = self.dialog.getAvailableValuesOfType( (QgsProcessingParameterRasterLayer, QgsProcessingParameterMultipleLayers), [QgsProcessingOutputRasterLayer, QgsProcessingOutputMapLayer, QgsProcessingOutputMultipleLayers]) - elif self.param.layerType() == QgsProcessing.TypeVector: + elif self.parameterDefinition().layerType() == QgsProcessing.TypeVector: options = self.dialog.getAvailableValuesOfType((QgsProcessingParameterFeatureSource, QgsProcessingParameterVectorLayer, QgsProcessingParameterMultipleLayers), @@ -645,40 +645,40 @@ class MultipleLayerWidgetWrapper(WidgetWrapper): def createWidget(self): if self.dialogType == DIALOG_STANDARD: - if self.param.layerType() == QgsProcessing.TypeFile: + if self.parameterDefinition().layerType() == QgsProcessing.TypeFile: return MultipleInputPanel(datatype=QgsProcessing.TypeFile) else: - if self.param.layerType() == QgsProcessing.TypeRaster: + if self.parameterDefinition().layerType() == QgsProcessing.TypeRaster: options = QgsProcessingUtils.compatibleRasterLayers(QgsProject.instance(), False) - elif self.param.layerType() in (QgsProcessing.TypeVectorAnyGeometry, QgsProcessing.TypeVector): + elif self.parameterDefinition().layerType() in (QgsProcessing.TypeVectorAnyGeometry, QgsProcessing.TypeVector): options = QgsProcessingUtils.compatibleVectorLayers(QgsProject.instance(), [], False) - elif self.param.layerType() == QgsProcessing.TypeMapLayer: + elif self.parameterDefinition().layerType() == QgsProcessing.TypeMapLayer: options = QgsProcessingUtils.compatibleVectorLayers(QgsProject.instance(), [], False) options.extend(QgsProcessingUtils.compatibleRasterLayers(QgsProject.instance(), False)) else: - options = QgsProcessingUtils.compatibleVectorLayers(QgsProject.instance(), [self.param.layerType()], + options = QgsProcessingUtils.compatibleVectorLayers(QgsProject.instance(), [self.parameterDefinition().layerType()], False) opts = [getExtendedLayerName(opt) for opt in options] - return MultipleInputPanel(opts, datatype=self.param.layerType()) + return MultipleInputPanel(opts, datatype=self.parameterDefinition().layerType()) elif self.dialogType == DIALOG_BATCH: - widget = BatchInputSelectionPanel(self.param, self.row, self.col, self.dialog) + widget = BatchInputSelectionPanel(self.parameterDefinition(), self.row, self.col, self.dialog) widget.valueChanged.connect(lambda: self.widgetValueHasChanged.emit(self)) return widget else: options = [self.dialog.resolveValueDescription(opt) for opt in self._getOptions()] - return MultipleInputPanel(options, datatype=self.param.layerType()) + return MultipleInputPanel(options, datatype=self.parameterDefinition().layerType()) def refresh(self): - if self.param.layerType() != QgsProcessing.TypeFile: - if self.param.layerType() == QgsProcessing.TypeRaster: + if self.parameterDefinition().layerType() != QgsProcessing.TypeFile: + if self.parameterDefinition().layerType() == QgsProcessing.TypeRaster: options = QgsProcessingUtils.compatibleRasterLayers(QgsProject.instance(), False) - elif self.param.layerType() in (QgsProcessing.TypeVectorAnyGeometry, QgsProcessing.TypeVector): + elif self.parameterDefinition().layerType() in (QgsProcessing.TypeVectorAnyGeometry, QgsProcessing.TypeVector): options = QgsProcessingUtils.compatibleVectorLayers(QgsProject.instance(), [], False) - elif self.param.layerType() == QgsProcessing.TypeMapLayer: + elif self.parameterDefinition().layerType() == QgsProcessing.TypeMapLayer: options = QgsProcessingUtils.compatibleVectorLayers(QgsProject.instance(), [], False) options.extend(QgsProcessingUtils.compatibleRasterLayers(QgsProject.instance(), False)) else: - options = QgsProcessingUtils.compatibleVectorLayers(QgsProject.instance(), [self.param.layerType()], + options = QgsProcessingUtils.compatibleVectorLayers(QgsProject.instance(), [self.parameterDefinition().layerType()], False) opts = [getExtendedLayerName(opt) for opt in options] self.widget.updateForOptions(opts) @@ -710,18 +710,18 @@ class MultipleLayerWidgetWrapper(WidgetWrapper): def value(self): if self.dialogType == DIALOG_STANDARD: - if self.param.layerType() == QgsProcessing.TypeFile: - return self.param.setValue(self.widget.selectedoptions) + if self.parameterDefinition().layerType() == QgsProcessing.TypeFile: + return self.parameterDefinition().setValue(self.widget.selectedoptions) else: - if self.param.layerType() == QgsProcessing.TypeRaster: + if self.parameterDefinition().layerType() == QgsProcessing.TypeRaster: options = QgsProcessingUtils.compatibleRasterLayers(QgsProject.instance(), False) - elif self.param.layerType() in (QgsProcessing.TypeVectorAnyGeometry, QgsProcessing.TypeVector): + elif self.parameterDefinition().layerType() in (QgsProcessing.TypeVectorAnyGeometry, QgsProcessing.TypeVector): options = QgsProcessingUtils.compatibleVectorLayers(QgsProject.instance(), [], False) - elif self.param.layerType() == QgsProcessing.TypeMapLayer: + elif self.parameterDefinition().layerType() == QgsProcessing.TypeMapLayer: options = QgsProcessingUtils.compatibleVectorLayers(QgsProject.instance(), [], False) options.extend(QgsProcessingUtils.compatibleRasterLayers(QgsProject.instance(), False)) else: - options = QgsProcessingUtils.compatibleVectorLayers(QgsProject.instance(), [self.param.layerType()], + options = QgsProcessingUtils.compatibleVectorLayers(QgsProject.instance(), [self.parameterDefinition().layerType()], False) return [options[i] if isinstance(i, int) else i for i in self.widget.selectedoptions] elif self.dialogType == DIALOG_BATCH: @@ -730,7 +730,7 @@ class MultipleLayerWidgetWrapper(WidgetWrapper): options = self._getOptions() values = [options[i] if isinstance(i, int) else QgsProcessingModelChildParameterSource.fromStaticValue(i) for i in self.widget.selectedoptions] - if len(values) == 0 and not self.param.flags() & QgsProcessingParameterDefinition.FlagOptional: + if len(values) == 0 and not self.parameterDefinition().flags() & QgsProcessingParameterDefinition.FlagOptional: raise InvalidParameterValue() return values @@ -739,11 +739,11 @@ class NumberWidgetWrapper(WidgetWrapper): def createWidget(self): if self.dialogType in (DIALOG_STANDARD, DIALOG_BATCH): - widget = NumberInputPanel(self.param) + widget = NumberInputPanel(self.parameterDefinition()) widget.hasChanged.connect(lambda: self.widgetValueHasChanged.emit(self)) return widget else: - return ModelerNumberInputPanel(self.param, self.dialog) + return ModelerNumberInputPanel(self.parameterDefinition(), self.dialog) def setValue(self, value): if value is None or value == NULL: @@ -755,9 +755,9 @@ class NumberWidgetWrapper(WidgetWrapper): return self.widget.getValue() def postInitialize(self, wrappers): - if self.dialogType in (DIALOG_STANDARD, DIALOG_BATCH) and self.param.isDynamic(): + if self.dialogType in (DIALOG_STANDARD, DIALOG_BATCH) and self.parameterDefinition().isDynamic(): for wrapper in wrappers: - if wrapper.param.name() == self.param.dynamicLayerParameterName(): + if wrapper.parameterDefinition().name() == self.parameterDefinition().dynamicLayerParameterName(): self.widget.setDynamicLayer(wrapper.value()) wrapper.widgetValueHasChanged.connect(self.parentLayerChanged) break @@ -770,11 +770,11 @@ class DistanceWidgetWrapper(WidgetWrapper): def createWidget(self): if self.dialogType in (DIALOG_STANDARD, DIALOG_BATCH): - widget = DistanceInputPanel(self.param) + widget = DistanceInputPanel(self.parameterDefinition()) widget.hasChanged.connect(lambda: self.widgetValueHasChanged.emit(self)) return widget else: - return ModelerNumberInputPanel(self.param, self.dialog) + return ModelerNumberInputPanel(self.parameterDefinition(), self.dialog) def setValue(self, value): if value is None or value == NULL: @@ -788,10 +788,10 @@ class DistanceWidgetWrapper(WidgetWrapper): def postInitialize(self, wrappers): if self.dialogType in (DIALOG_STANDARD, DIALOG_BATCH): for wrapper in wrappers: - if wrapper.param.name() == self.param.dynamicLayerParameterName(): + if wrapper.parameterDefinition().name() == self.parameterDefinition().dynamicLayerParameterName(): self.widget.setDynamicLayer(wrapper.value()) wrapper.widgetValueHasChanged.connect(self.dynamicLayerChanged) - if wrapper.param.name() == self.param.parentParameterName(): + if wrapper.parameterDefinition().name() == self.parameterDefinition().parentParameterName(): self.widget.setUnitParameterValue(wrapper.value()) wrapper.widgetValueHasChanged.connect(self.parentParameterChanged) @@ -806,7 +806,7 @@ class RangeWidgetWrapper(WidgetWrapper): def createWidget(self): if self.dialogType in (DIALOG_STANDARD, DIALOG_BATCH): - widget = RangePanel(self.param) + widget = RangePanel(self.parameterDefinition()) widget.hasChanged.connect(lambda: self.widgetValueHasChanged.emit(self)) return widget #else: @@ -851,7 +851,7 @@ class MapLayerWidgetWrapper(WidgetWrapper): except: pass - if self.param.flags() & QgsProcessingParameterDefinition.FlagOptional: + if self.parameterDefinition().flags() & QgsProcessingParameterDefinition.FlagOptional: self.combo.setAllowEmptyLayer(True) self.combo.setLayer(None) @@ -859,14 +859,14 @@ class MapLayerWidgetWrapper(WidgetWrapper): self.combo.currentTextChanged.connect(lambda: self.widgetValueHasChanged.emit(self)) return widget elif self.dialogType == DIALOG_BATCH: - widget = BatchInputSelectionPanel(self.param, self.row, self.col, self.dialog) + widget = BatchInputSelectionPanel(self.parameterDefinition(), self.row, self.col, self.dialog) widget.valueChanged.connect(lambda: self.widgetValueHasChanged.emit(self)) return widget else: self.combo = QComboBox() layers = self.getAvailableLayers() self.combo.setEditable(True) - if self.param.flags() & QgsProcessingParameterDefinition.FlagOptional: + if self.parameterDefinition().flags() & QgsProcessingParameterDefinition.FlagOptional: self.combo.addItem(self.NOT_SELECTED, self.NOT_SET_OPTION) for layer in layers: self.combo.addItem(self.dialog.resolveValueDescription(layer), layer) @@ -949,7 +949,7 @@ class MapLayerWidgetWrapper(WidgetWrapper): else: def validator(v): if not bool(v): - return self.param.flags() & QgsProcessingParameterDefinition.FlagOptional + return self.parameterDefinition().flags() & QgsProcessingParameterDefinition.FlagOptional else: return os.path.exists(v) @@ -969,7 +969,7 @@ class RasterWidgetWrapper(MapLayerWidgetWrapper): def selectFile(self): filename, selected_filter = self.getFileName(self.combo.currentText()) if filename: - filename = dataobjects.getRasterSublayer(filename, self.param) + filename = dataobjects.getRasterSublayer(filename, self.parameterDefinition()) if isinstance(self.combo, QgsMapLayerComboBox): items = self.combo.additionalItems() items.append(filename) @@ -987,23 +987,23 @@ class EnumWidgetWrapper(WidgetWrapper): if self.dialogType in (DIALOG_STANDARD, DIALOG_BATCH): self._useCheckBoxes = useCheckBoxes if self._useCheckBoxes and not self.dialogType == DIALOG_BATCH: - return CheckboxesPanel(options=self.param.options(), - multiple=self.param.allowMultiple(), + return CheckboxesPanel(options=self.parameterDefinition().options(), + multiple=self.parameterDefinition().allowMultiple(), columns=columns) - if self.param.allowMultiple(): - return MultipleInputPanel(options=self.param.options()) + if self.parameterDefinition().allowMultiple(): + return MultipleInputPanel(options=self.parameterDefinition().options()) else: widget = QComboBox() - for i, option in enumerate(self.param.options()): + for i, option in enumerate(self.parameterDefinition().options()): widget.addItem(option, i) - if self.param.defaultValue(): - widget.setCurrentIndex(widget.findData(self.param.defaultValue())) + if self.parameterDefinition().defaultValue(): + widget.setCurrentIndex(widget.findData(self.parameterDefinition().defaultValue())) return widget else: self.combobox = QComboBox() - if self.param.flags() & QgsProcessingParameterDefinition.FlagOptional: + if self.parameterDefinition().flags() & QgsProcessingParameterDefinition.FlagOptional: self.combobox.addItem(self.NOT_SELECTED, self.NOT_SET_OPTION) - for i, option in enumerate(self.param.options()): + for i, option in enumerate(self.parameterDefinition().options()): self.combobox.addItem(option, i) values = self.dialog.getAvailableValuesOfType(QgsProcessingParameterEnum) for v in values: @@ -1018,7 +1018,7 @@ class EnumWidgetWrapper(WidgetWrapper): if self._useCheckBoxes and not self.dialogType == DIALOG_BATCH: self.widget.setValue(value) return - if self.param.allowMultiple(): + if self.parameterDefinition().allowMultiple(): self.widget.setSelectedItems(value) else: self.widget.setCurrentIndex(self.widget.findData(value)) @@ -1029,7 +1029,7 @@ class EnumWidgetWrapper(WidgetWrapper): if self.dialogType in (DIALOG_STANDARD, DIALOG_BATCH): if self._useCheckBoxes and not self.dialogType == DIALOG_BATCH: return self.widget.value() - if self.param.allowMultiple(): + if self.parameterDefinition().allowMultiple(): return self.widget.selectedoptions else: return self.widget.currentData() @@ -1071,13 +1071,13 @@ class FeatureSourceWidgetWrapper(WidgetWrapper): widget.setLayout(vl) filters = QgsMapLayerProxyModel.Filters() - if QgsProcessing.TypeVectorAnyGeometry in self.param.dataTypes() or len(self.param.dataTypes()) == 0: + if QgsProcessing.TypeVectorAnyGeometry in self.parameterDefinition().dataTypes() or len(self.parameterDefinition().dataTypes()) == 0: filters = QgsMapLayerProxyModel.HasGeometry - if QgsProcessing.TypeVectorPoint in self.param.dataTypes(): + if QgsProcessing.TypeVectorPoint in self.parameterDefinition().dataTypes(): filters |= QgsMapLayerProxyModel.PointLayer - if QgsProcessing.TypeVectorLine in self.param.dataTypes(): + if QgsProcessing.TypeVectorLine in self.parameterDefinition().dataTypes(): filters |= QgsMapLayerProxyModel.LineLayer - if QgsProcessing.TypeVectorPolygon in self.param.dataTypes(): + if QgsProcessing.TypeVectorPolygon in self.parameterDefinition().dataTypes(): filters |= QgsMapLayerProxyModel.PolygonLayer if not filters: filters = QgsMapLayerProxyModel.VectorLayer @@ -1097,7 +1097,7 @@ class FeatureSourceWidgetWrapper(WidgetWrapper): self.combo.setFilters(filters) self.combo.setExcludedProviders(['grass']) - if self.param.flags() & QgsProcessingParameterDefinition.FlagOptional: + if self.parameterDefinition().flags() & QgsProcessingParameterDefinition.FlagOptional: self.combo.setAllowEmptyLayer(True) self.combo.setLayer(None) @@ -1105,18 +1105,18 @@ class FeatureSourceWidgetWrapper(WidgetWrapper): return widget elif self.dialogType == DIALOG_BATCH: - widget = BatchInputSelectionPanel(self.param, self.row, self.col, self.dialog) + widget = BatchInputSelectionPanel(self.parameterDefinition(), self.row, self.col, self.dialog) widget.valueChanged.connect(lambda: self.widgetValueHasChanged.emit(self)) return widget else: self.combo = QComboBox() layers = self.dialog.getAvailableValuesOfType( (QgsProcessingParameterFeatureSource, QgsProcessingParameterVectorLayer), - (QgsProcessingOutputVectorLayer, QgsProcessingOutputMapLayer, QgsProcessingOutputString, QgsProcessingOutputFile), self.param.dataTypes()) + (QgsProcessingOutputVectorLayer, QgsProcessingOutputMapLayer, QgsProcessingOutputString, QgsProcessingOutputFile), self.parameterDefinition().dataTypes()) self.combo.setEditable(True) for layer in layers: self.combo.addItem(self.dialog.resolveValueDescription(layer), layer) - if self.param.flags() & QgsProcessingParameterDefinition.FlagOptional: + if self.parameterDefinition().flags() & QgsProcessingParameterDefinition.FlagOptional: self.combo.setEditText("") widget = QWidget() @@ -1144,7 +1144,7 @@ class FeatureSourceWidgetWrapper(WidgetWrapper): def selectFile(self): filename, selected_filter = self.getFileName(self.combo.currentText()) if filename: - filename = dataobjects.getRasterSublayer(filename, self.param) + filename = dataobjects.getRasterSublayer(filename, self.parameterDefinition()) if isinstance(self.combo, QgsMapLayerComboBox): items = self.combo.additionalItems() items.append(filename) @@ -1208,7 +1208,7 @@ class FeatureSourceWidgetWrapper(WidgetWrapper): else: def validator(v): if not bool(v): - return self.param.flags() & QgsProcessingParameterDefinition.FlagOptional + return self.parameterDefinition().flags() & QgsProcessingParameterDefinition.FlagOptional else: return os.path.exists(v) @@ -1222,7 +1222,7 @@ class StringWidgetWrapper(WidgetWrapper): def createWidget(self): if self.dialogType == DIALOG_STANDARD: - if self.param.multiLine(): + if self.parameterDefinition().multiLine(): widget = QPlainTextEdit() else: self._lineedit = QLineEdit() @@ -1238,7 +1238,7 @@ class StringWidgetWrapper(WidgetWrapper): QgsProcessingParameterField, QgsProcessingParameterExpression], [QgsProcessingOutputString, QgsProcessingOutputFile]) options = [(self.dialog.resolveValueDescription(s), s) for s in strings] - if self.param.multiLine(): + if self.parameterDefinition().multiLine(): widget = MultilineTextPanel(options) else: widget = QComboBox() @@ -1267,7 +1267,7 @@ class StringWidgetWrapper(WidgetWrapper): return if self.dialogType == DIALOG_STANDARD: - if self.param.multiLine(): + if self.parameterDefinition().multiLine(): self.widget.setPlainText(value) else: self._lineedit.setText(value) @@ -1276,14 +1276,14 @@ class StringWidgetWrapper(WidgetWrapper): self.widget.setText(value) else: - if self.param.multiLine(): + if self.parameterDefinition().multiLine(): self.widget.setValue(value) else: self.setComboValue(value) def value(self): if self.dialogType in DIALOG_STANDARD: - if self.param.multiLine(): + if self.parameterDefinition().multiLine(): text = self.widget.toPlainText() else: text = self._lineedit.text() @@ -1293,12 +1293,12 @@ class StringWidgetWrapper(WidgetWrapper): return self.widget.text() else: - if self.param.multiLine(): + if self.parameterDefinition().multiLine(): value = self.widget.getValue() option = self.widget.getOption() if option == MultilineTextPanel.USE_TEXT: if value == '': - if self.param.flags() & QgsProcessingParameterDefinition.FlagOptional: + if self.parameterDefinition().flags() & QgsProcessingParameterDefinition.FlagOptional: return None else: raise InvalidParameterValue() @@ -1308,7 +1308,7 @@ class StringWidgetWrapper(WidgetWrapper): return value else: def validator(v): - return bool(v) or self.param.flags() & QgsProcessingParameterDefinition.FlagOptional + return bool(v) or self.parameterDefinition().flags() & QgsProcessingParameterDefinition.FlagOptional return self.comboValue(validator) @@ -1321,12 +1321,12 @@ class ExpressionWidgetWrapper(WidgetWrapper): def createWidget(self): if self.dialogType in (DIALOG_STANDARD, DIALOG_BATCH): - if self.param.parentLayerParameterName(): + if self.parameterDefinition().parentLayerParameterName(): widget = QgsFieldExpressionWidget() else: widget = QgsExpressionLineEdit() - if self.param.defaultValue(): - widget.setExpression(self.param.defaultValue()) + if self.parameterDefinition().defaultValue(): + widget.setExpression(self.parameterDefinition().defaultValue()) else: strings = self.dialog.getAvailableValuesOfType( [QgsProcessingParameterExpression, QgsProcessingParameterString, QgsProcessingParameterNumber, QgsProcessingParameterDistance], @@ -1336,12 +1336,12 @@ class ExpressionWidgetWrapper(WidgetWrapper): widget.setEditable(True) for desc, val in options: widget.addItem(desc, val) - widget.setEditText(self.param.defaultValue() or "") + widget.setEditText(self.parameterDefinition().defaultValue() or "") return widget def postInitialize(self, wrappers): for wrapper in wrappers: - if wrapper.param.name() == self.param.parentLayerParameterName(): + if wrapper.parameterDefinition().name() == self.parameterDefinition().parentLayerParameterName(): if self.dialogType in (DIALOG_STANDARD, DIALOG_BATCH): self.setLayer(wrapper.value()) wrapper.widgetValueHasChanged.connect(self.parentLayerChanged) @@ -1374,7 +1374,7 @@ class ExpressionWidgetWrapper(WidgetWrapper): return self.widget.expression() else: def validator(v): - return bool(v) or self.param.flags() & QgsProcessingParameterDefinition.FlagOptional + return bool(v) or self.parameterDefinition().flags() & QgsProcessingParameterDefinition.FlagOptional return self.comboValue(validator) @@ -1403,13 +1403,13 @@ class VectorLayerWidgetWrapper(WidgetWrapper): self.combo.setShowCrs(True) filters = QgsMapLayerProxyModel.Filters() - if QgsProcessing.TypeVectorAnyGeometry in self.param.dataTypes() or len(self.param.dataTypes()) == 0: + if QgsProcessing.TypeVectorAnyGeometry in self.parameterDefinition().dataTypes() or len(self.parameterDefinition().dataTypes()) == 0: filters = QgsMapLayerProxyModel.HasGeometry - if QgsProcessing.TypeVectorPoint in self.param.dataTypes(): + if QgsProcessing.TypeVectorPoint in self.parameterDefinition().dataTypes(): filters |= QgsMapLayerProxyModel.PointLayer - if QgsProcessing.TypeVectorLine in self.param.dataTypes(): + if QgsProcessing.TypeVectorLine in self.parameterDefinition().dataTypes(): filters |= QgsMapLayerProxyModel.LineLayer - if QgsProcessing.TypeVectorPolygon in self.param.dataTypes(): + if QgsProcessing.TypeVectorPolygon in self.parameterDefinition().dataTypes(): filters |= QgsMapLayerProxyModel.PolygonLayer if not filters: filters = QgsMapLayerProxyModel.VectorLayer @@ -1424,7 +1424,7 @@ class VectorLayerWidgetWrapper(WidgetWrapper): except: pass - if self.param.flags() & QgsProcessingParameterDefinition.FlagOptional: + if self.parameterDefinition().flags() & QgsProcessingParameterDefinition.FlagOptional: self.combo.setAllowEmptyLayer(True) self.combo.setLayer(None) @@ -1433,7 +1433,7 @@ class VectorLayerWidgetWrapper(WidgetWrapper): return widget elif self.dialogType == DIALOG_BATCH: - widget = BatchInputSelectionPanel(self.param, self.row, self.col, self.dialog) + widget = BatchInputSelectionPanel(self.parameterDefinition(), self.row, self.col, self.dialog) widget.valueChanged.connect(lambda: self.widgetValueHasChanged.emit(self)) return widget else: @@ -1441,7 +1441,7 @@ class VectorLayerWidgetWrapper(WidgetWrapper): self.combo.setEditable(True) tables = self.dialog.getAvailableValuesOfType((QgsProcessingParameterVectorLayer, QgsProcessingParameterString), (QgsProcessingOutputVectorLayer, QgsProcessingOutputMapLayer, QgsProcessingOutputFile, QgsProcessingOutputString)) - if self.param.flags() & QgsProcessingParameterDefinition.FlagOptional: + if self.parameterDefinition().flags() & QgsProcessingParameterDefinition.FlagOptional: self.combo.addItem(self.NOT_SELECTED, self.NOT_SET_OPTION) for table in tables: self.combo.addItem(self.dialog.resolveValueDescription(table), table) @@ -1463,7 +1463,7 @@ class VectorLayerWidgetWrapper(WidgetWrapper): def selectFile(self): filename, selected_filter = self.getFileName(self.combo.currentText()) if filename: - filename = dataobjects.getRasterSublayer(filename, self.param) + filename = dataobjects.getRasterSublayer(filename, self.parameterDefinition()) if isinstance(self.combo, QgsMapLayerComboBox): items = self.combo.additionalItems() items.append(filename) @@ -1516,7 +1516,7 @@ class VectorLayerWidgetWrapper(WidgetWrapper): return self.widget.value() else: def validator(v): - return bool(v) or self.param.flags() & QgsProcessingParameterDefinition.FlagOptional + return bool(v) or self.parameterDefinition().flags() & QgsProcessingParameterDefinition.FlagOptional return self.comboValue(validator, combobox=self.combo) @@ -1532,17 +1532,17 @@ class TableFieldWidgetWrapper(WidgetWrapper): self._layer = None if self.dialogType in (DIALOG_STANDARD, DIALOG_BATCH): - if self.param.allowMultiple(): + if self.parameterDefinition().allowMultiple(): return MultipleInputPanel(options=[]) else: widget = QgsFieldComboBox() - widget.setAllowEmptyFieldName(self.param.flags() & QgsProcessingParameterDefinition.FlagOptional) + widget.setAllowEmptyFieldName(self.parameterDefinition().flags() & QgsProcessingParameterDefinition.FlagOptional) widget.fieldChanged.connect(lambda: self.widgetValueHasChanged.emit(self)) - if self.param.dataType() == QgsProcessingParameterField.Numeric: + if self.parameterDefinition().dataType() == QgsProcessingParameterField.Numeric: widget.setFilters(QgsFieldProxyModel.Numeric) - elif self.param.dataType() == QgsProcessingParameterField.String: + elif self.parameterDefinition().dataType() == QgsProcessingParameterField.String: widget.setFilters(QgsFieldProxyModel.String) - elif self.param.dataType() == QgsProcessingParameterField.DateTime: + elif self.parameterDefinition().dataType() == QgsProcessingParameterField.DateTime: widget.setFilters(QgsFieldProxyModel.Date | QgsFieldProxyModel.Time) return widget else: @@ -1550,7 +1550,7 @@ class TableFieldWidgetWrapper(WidgetWrapper): widget.setEditable(True) fields = self.dialog.getAvailableValuesOfType([QgsProcessingParameterField, QgsProcessingParameterString], [QgsProcessingOutputString]) - if self.param.flags() & QgsProcessingParameterDefinition.FlagOptional: + if self.parameterDefinition().flags() & QgsProcessingParameterDefinition.FlagOptional: widget.addItem(self.NOT_SET, self.NOT_SET_OPTION) for f in fields: widget.addItem(self.dialog.resolveValueDescription(f), f) @@ -1561,7 +1561,7 @@ class TableFieldWidgetWrapper(WidgetWrapper): def postInitialize(self, wrappers): for wrapper in wrappers: - if wrapper.param.name() == self.param.parentLayerParameterName(): + if wrapper.parameterDefinition().name() == self.parameterDefinition().parentLayerParameterName(): if self.dialogType in (DIALOG_STANDARD, DIALOG_BATCH): self.setLayer(wrapper.value()) wrapper.widgetValueHasChanged.connect(self.parentValueChanged) @@ -1579,24 +1579,24 @@ class TableFieldWidgetWrapper(WidgetWrapper): self.refreshItems() def refreshItems(self): - if self.param.allowMultiple(): + if self.parameterDefinition().allowMultiple(): self.widget.updateForOptions(self.getFields()) else: self.widget.setLayer(self._layer) self.widget.setCurrentIndex(0) - if self.param.defaultValue() is not None: - self.setValue(self.param.defaultValue()) + if self.parameterDefinition().defaultValue() is not None: + self.setValue(self.parameterDefinition().defaultValue()) def getFields(self): if self._layer is None: return [] fieldTypes = [] - if self.param.dataType() == QgsProcessingParameterField.String: + if self.parameterDefinition().dataType() == QgsProcessingParameterField.String: fieldTypes = [QVariant.String] - elif self.param.dataType() == QgsProcessingParameterField.Numeric: + elif self.parameterDefinition().dataType() == QgsProcessingParameterField.Numeric: fieldTypes = [QVariant.Int, QVariant.Double, QVariant.LongLong, QVariant.UInt, QVariant.ULongLong] - elif self.param.dataType() == QgsProcessingParameterField.DateTime: + elif self.parameterDefinition().dataType() == QgsProcessingParameterField.DateTime: fieldTypes = [QVariant.Date, QVariant.Time, QVariant.DateTime] fieldNames = [] @@ -1610,7 +1610,7 @@ class TableFieldWidgetWrapper(WidgetWrapper): return if self.dialogType in (DIALOG_STANDARD, DIALOG_BATCH): - if self.param.allowMultiple(): + if self.parameterDefinition().allowMultiple(): options = self.widget.options selected = [] if isinstance(value, str): @@ -1632,16 +1632,16 @@ class TableFieldWidgetWrapper(WidgetWrapper): def value(self): if self.dialogType in (DIALOG_STANDARD, DIALOG_BATCH): - if self.param.allowMultiple(): + if self.parameterDefinition().allowMultiple(): return [self.widget.options[i] for i in self.widget.selectedoptions] else: f = self.widget.currentField() - if self.param.flags() & QgsProcessingParameterDefinition.FlagOptional and not f: + if self.parameterDefinition().flags() & QgsProcessingParameterDefinition.FlagOptional and not f: return None return f else: def validator(v): - return bool(v) or self.param.flags() & QgsProcessingParameterDefinition.FlagOptional + return bool(v) or self.parameterDefinition().flags() & QgsProcessingParameterDefinition.FlagOptional return self.comboValue(validator) @@ -1657,10 +1657,10 @@ class BandWidgetWrapper(WidgetWrapper): self._layer = None if self.dialogType in (DIALOG_STANDARD, DIALOG_BATCH): - if self.param.allowMultiple(): + if self.parameterDefinition().allowMultiple(): return MultipleInputPanel(options=[]) widget = QgsRasterBandComboBox() - widget.setShowNotSetOption(self.param.flags() & QgsProcessingParameterDefinition.FlagOptional) + widget.setShowNotSetOption(self.parameterDefinition().flags() & QgsProcessingParameterDefinition.FlagOptional) widget.bandChanged.connect(lambda: self.widgetValueHasChanged.emit(self)) return widget else: @@ -1668,7 +1668,7 @@ class BandWidgetWrapper(WidgetWrapper): widget.setEditable(True) fields = self.dialog.getAvailableValuesOfType([QgsProcessingParameterBand, QgsProcessingParameterDistance, QgsProcessingParameterNumber], [QgsProcessingOutputNumber]) - if self.param.flags() & QgsProcessingParameterDefinition.FlagOptional: + if self.parameterDefinition().flags() & QgsProcessingParameterDefinition.FlagOptional: widget.addItem(self.NOT_SET, self.NOT_SET_OPTION) for f in fields: widget.addItem(self.dialog.resolveValueDescription(f), f) @@ -1676,7 +1676,7 @@ class BandWidgetWrapper(WidgetWrapper): def postInitialize(self, wrappers): for wrapper in wrappers: - if wrapper.param.name() == self.param.parentLayerParameterName(): + if wrapper.parameterDefinition().name() == self.parameterDefinition().parentLayerParameterName(): if self.dialogType in (DIALOG_STANDARD, DIALOG_BATCH): self.setLayer(wrapper.value()) wrapper.widgetValueHasChanged.connect(self.parentValueChanged) @@ -1719,7 +1719,7 @@ class BandWidgetWrapper(WidgetWrapper): return if self.dialogType in (DIALOG_STANDARD, DIALOG_BATCH): - if self.param.allowMultiple(): + if self.parameterDefinition().allowMultiple(): options = self.widget.options selected = [] if isinstance(value, str): @@ -1739,7 +1739,7 @@ class BandWidgetWrapper(WidgetWrapper): def value(self): if self.dialogType in (DIALOG_STANDARD, DIALOG_BATCH): - if self.param.allowMultiple(): + if self.parameterDefinition().allowMultiple(): bands = [] for i in self.widget.selectedoptions: match = re.search('(?:\A|[^0-9])([0-9]+)(?:\Z|[^0-9]|)', self.widget.options[i]) @@ -1748,12 +1748,12 @@ class BandWidgetWrapper(WidgetWrapper): return bands else: f = self.widget.currentBand() - if self.param.flags() & QgsProcessingParameterDefinition.FlagOptional and not f: + if self.parameterDefinition().flags() & QgsProcessingParameterDefinition.FlagOptional and not f: return None return f else: def validator(v): - return bool(v) or self.param.flags() & QgsProcessingParameterDefinition.FlagOptional + return bool(v) or self.parameterDefinition().flags() & QgsProcessingParameterDefinition.FlagOptional return self.comboValue(validator) @@ -1770,6 +1770,14 @@ class WidgetWrapperFactory: if param.metadata().get('widget_wrapper', None) is not None: return WidgetWrapperFactory.create_wrapper_from_metadata(param, dialog, row, col) else: + # try from c++ registry first + dialog_type = dialogTypes.get(dialog.__class__.__name__, + QgsAbstractProcessingParameterWidgetWrapper.Standard) + wrapper = QgsGui.processingGuiRegistry().createParameterWidgetWrapper(param, dialog_type) + if wrapper is not None: + return wrapper + + # fallback to Python registry return WidgetWrapperFactory.create_wrapper_from_class(param, dialog, row, col) @staticmethod diff --git a/python/plugins/processing/gui/wrappers_postgis.py b/python/plugins/processing/gui/wrappers_postgis.py index 4d1221f11cd..da5cf61b65c 100644 --- a/python/plugins/processing/gui/wrappers_postgis.py +++ b/python/plugins/processing/gui/wrappers_postgis.py @@ -89,7 +89,7 @@ class SchemaWidgetWrapper(WidgetWrapper): def postInitialize(self, wrappers): for wrapper in wrappers: - if wrapper.param.name() == self._connection_param: + if wrapper.parameterDefinition().name() == self._connection_param: self.connection_wrapper = wrapper self.setConnection(wrapper.value()) wrapper.widgetValueHasChanged.connect(self.connectionChanged) @@ -161,7 +161,7 @@ class TableWidgetWrapper(WidgetWrapper): def postInitialize(self, wrappers): for wrapper in wrappers: - if wrapper.param.name() == self._schema_param: + if wrapper.parameterDefinition().name() == self._schema_param: self.schema_wrapper = wrapper self.setSchema(wrapper.database(), wrapper.value()) wrapper.widgetValueHasChanged.connect(self.schemaChanged) diff --git a/src/gui/CMakeLists.txt b/src/gui/CMakeLists.txt index 22b5920db3e..f8b6b8518a2 100755 --- a/src/gui/CMakeLists.txt +++ b/src/gui/CMakeLists.txt @@ -200,6 +200,8 @@ SET(QGIS_GUI_SRCS processing/qgsprocessingrecentalgorithmlog.cpp processing/qgsprocessingtoolboxmodel.cpp processing/qgsprocessingtoolboxtreeview.cpp + processing/qgsprocessingwidgetwrapper.cpp + processing/qgsprocessingwidgetwrapperimpl.cpp qgisinterface.cpp qgsactionmenu.cpp @@ -726,6 +728,8 @@ SET(QGIS_GUI_MOC_HDRS processing/qgsprocessingrecentalgorithmlog.h processing/qgsprocessingtoolboxmodel.h processing/qgsprocessingtoolboxtreeview.h + processing/qgsprocessingwidgetwrapper.h + processing/qgsprocessingwidgetwrapperimpl.h ) SET_PROPERTY(GLOBAL PROPERTY QGIS_GUI_MOC_HDRS ${QGIS_GUI_MOC_HDRS}) diff --git a/src/gui/processing/qgsprocessingguiregistry.cpp b/src/gui/processing/qgsprocessingguiregistry.cpp index dcedac93459..eb67c5ccd80 100644 --- a/src/gui/processing/qgsprocessingguiregistry.cpp +++ b/src/gui/processing/qgsprocessingguiregistry.cpp @@ -18,17 +18,26 @@ #include "qgsprocessingguiregistry.h" #include "qgsprocessingalgorithmconfigurationwidget.h" #include "qgsprocessingconfigurationwidgets.h" +#include "qgsprocessingwidgetwrapperimpl.h" +#include "qgsprocessingparameters.h" #include "qgis.h" +#include "qgslogger.h" QgsProcessingGuiRegistry::QgsProcessingGuiRegistry() { addAlgorithmConfigurationWidgetFactory( new QgsFilterAlgorithmConfigurationWidgetFactory() ); + + addParameterWidgetFactory( new QgsProcessingBooleanWidgetWrapper() ); } QgsProcessingGuiRegistry::~QgsProcessingGuiRegistry() { - for ( QgsProcessingAlgorithmConfigurationWidgetFactory *factory : qgis::as_const( mAlgorithmConfigurationWidgetFactories ) ) + const QList< QgsProcessingAlgorithmConfigurationWidgetFactory * > factories = mAlgorithmConfigurationWidgetFactories; + for ( QgsProcessingAlgorithmConfigurationWidgetFactory *factory : factories ) removeAlgorithmConfigurationWidgetFactory( factory ); + const QMap< QString, QgsProcessingParameterWidgetFactoryInterface * > paramFactories = mParameterWidgetFactories; + for ( auto it = paramFactories.constBegin(); it != paramFactories.constEnd(); ++it ) + removeParameterWidgetFactory( it.value() ); } void QgsProcessingGuiRegistry::addAlgorithmConfigurationWidgetFactory( QgsProcessingAlgorithmConfigurationWidgetFactory *factory ) @@ -54,3 +63,44 @@ QgsProcessingAlgorithmConfigurationWidget *QgsProcessingGuiRegistry::algorithmCo return nullptr; } + +bool QgsProcessingGuiRegistry::addParameterWidgetFactory( QgsProcessingParameterWidgetFactoryInterface *factory ) +{ + if ( !factory ) + return false; + + if ( mParameterWidgetFactories.contains( factory->parameterType() ) ) + { + QgsLogger::warning( QStringLiteral( "Duplicate parameter factory for %1 registered" ).arg( factory->parameterType() ) ); + return false; + } + + mParameterWidgetFactories.insert( factory->parameterType(), factory ); + return true; +} + +void QgsProcessingGuiRegistry::removeParameterWidgetFactory( QgsProcessingParameterWidgetFactoryInterface *factory ) +{ + if ( !factory ) + return; + + mParameterWidgetFactories.remove( factory->parameterType() ); + delete factory; +} + +QgsAbstractProcessingParameterWidgetWrapper *QgsProcessingGuiRegistry::createParameterWidgetWrapper( const QgsProcessingParameterDefinition *parameter, QgsAbstractProcessingParameterWidgetWrapper::WidgetType type ) +{ + // TODO - support modeler type + if ( type == QgsAbstractProcessingParameterWidgetWrapper::Modeler ) + return nullptr; + + if ( !parameter ) + return nullptr; + + const QString parameterType = parameter->type(); + if ( !mParameterWidgetFactories.contains( parameterType ) ) + return nullptr; + + return mParameterWidgetFactories.value( parameterType )->createWidgetWrapper( parameter, type ); +} + diff --git a/src/gui/processing/qgsprocessingguiregistry.h b/src/gui/processing/qgsprocessingguiregistry.h index af742a16f5a..2799f0a8046 100644 --- a/src/gui/processing/qgsprocessingguiregistry.h +++ b/src/gui/processing/qgsprocessingguiregistry.h @@ -20,8 +20,9 @@ #include "qgis_gui.h" #include "qgis_sip.h" - +#include "qgsprocessingwidgetwrapper.h" #include