[processing] Initial work on porting base GUI classes to c++

- Add abstract base class for Processing widget wrappers to c++
- Add wrapper factory interface to c++
- Make QgsProcessingGuiRegistry also register widget wrapper
factories, and be responsible for creation of new c++
processing widget wrapper instances
- Start on private c++ implementation of boolean widget wrapper,
including unit tests
This commit is contained in:
Nyall Dawson 2018-08-14 16:41:11 +10:00
parent bb9117075d
commit 644ef6a752
21 changed files with 1293 additions and 189 deletions

View File

@ -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
};

View File

@ -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 *
************************************************************************/

View File

@ -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

View File

@ -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()

View File

@ -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)

View File

@ -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)

View File

@ -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

View File

@ -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 \

View File

@ -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():

View File

@ -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 '<p><b>{}</b></p><p>{}</p>'.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()])

View File

@ -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

View File

@ -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)

View File

@ -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})

View File

@ -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 );
}

View File

@ -20,8 +20,9 @@
#include "qgis_gui.h"
#include "qgis_sip.h"
#include "qgsprocessingwidgetwrapper.h"
#include <QList>
#include <QMap>
class QgsProcessingAlgorithm;
class QgsProcessingAlgorithmConfigurationWidget;
@ -31,6 +32,9 @@ class QgsProcessingAlgorithmConfigurationWidgetFactory;
* The QgsProcessingGuiRegistry is a home for widgets for processing
* configuration widgets.
*
* QgsProcessingGuiRegistry is not usually directly created, but rather accessed through
* QgsGui::processingGuiRegistry().
*
* \ingroup gui
* \since QGIS 3.2
*/
@ -70,9 +74,44 @@ class GUI_EXPORT QgsProcessingGuiRegistry
*/
QgsProcessingAlgorithmConfigurationWidget *algorithmConfigurationWidget( const QgsProcessingAlgorithm *algorithm ) const;
/**
* Adds a parameter widget \a factory to the registry. Ownership of \a 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.
*
* \see removeParameterWidgetFactory()
* \see createParameterWidgetWrapper()
*
* \since QGIS 3.4
*/
bool addParameterWidgetFactory( QgsProcessingParameterWidgetFactoryInterface *factory SIP_TRANSFER );
/**
* Removes a parameter widget \a factory from the registry. The factory will be deleted.
*
* \see addParameterWidgetFactory()
*
* \since QGIS 3.4
*/
void removeParameterWidgetFactory( QgsProcessingParameterWidgetFactoryInterface *factory );
/**
* Creates a new parameter widget wrapper for the given \a parameter. The \a 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 \a parameter, a nullptr will be returned.
*
* \see addParameterWidgetFactory()
*/
QgsAbstractProcessingParameterWidgetWrapper *createParameterWidgetWrapper( const QgsProcessingParameterDefinition *parameter, QgsAbstractProcessingParameterWidgetWrapper::WidgetType type ) SIP_FACTORY;
private:
QList <QgsProcessingAlgorithmConfigurationWidgetFactory *> mAlgorithmConfigurationWidgetFactories;
QMap< QString, QgsProcessingParameterWidgetFactoryInterface *> mParameterWidgetFactories;
};
#endif // QGSPROCESSINGGUIREGISTRY_H

View File

@ -0,0 +1,92 @@
/***************************************************************************
qgsprocessingwidgetwrapper.cpp
---------------------
begin : August 2018
copyright : (C) 2018 by Nyall Dawson
email : nyall dot dawson at gmail dot com
***************************************************************************/
/***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/
#include "qgsprocessingwidgetwrapper.h"
#include "qgsprocessingparameters.h"
#include <QLabel>
QgsAbstractProcessingParameterWidgetWrapper::QgsAbstractProcessingParameterWidgetWrapper( const QgsProcessingParameterDefinition *parameter, QgsAbstractProcessingParameterWidgetWrapper::WidgetType type, QObject *parent )
: QObject( parent )
, mType( type )
, mParameterDefinition( parameter )
{
}
QgsAbstractProcessingParameterWidgetWrapper::WidgetType QgsAbstractProcessingParameterWidgetWrapper::type() const
{
return mType;
}
QWidget *QgsAbstractProcessingParameterWidgetWrapper::createWrappedWidget( const QgsProcessingContext &context )
{
if ( mWidget )
return mWidget;
mWidget = createWidget();
setWidgetValue( mParameterDefinition->defaultValue(), context );
return mWidget;
}
QLabel *QgsAbstractProcessingParameterWidgetWrapper::createWrappedLabel()
{
if ( mLabel )
return mLabel;
mLabel = createLabel();
return mLabel;
}
QWidget *QgsAbstractProcessingParameterWidgetWrapper::wrappedWidget()
{
return mWidget;
}
QLabel *QgsAbstractProcessingParameterWidgetWrapper::wrappedLabel()
{
return mLabel;
}
const QgsProcessingParameterDefinition *QgsAbstractProcessingParameterWidgetWrapper::parameterDefinition() const
{
return mParameterDefinition;
}
QLabel *QgsAbstractProcessingParameterWidgetWrapper::createLabel()
{
switch ( mType )
{
case Batch:
return nullptr;
case Standard:
case Modeler:
{
std::unique_ptr< QLabel > label = qgis::make_unique< QLabel >( mParameterDefinition->description() );
label->setToolTip( mParameterDefinition->name() );
return label.release();
}
}
return nullptr;
}
void QgsAbstractProcessingParameterWidgetWrapper::postInitialize( const QList<QgsAbstractProcessingParameterWidgetWrapper *> & )
{
}

View File

@ -0,0 +1,212 @@
/***************************************************************************
qgsprocessingwidgetwrapper.h
---------------------
begin : August 2018
copyright : (C) 2018 by Nyall Dawson
email : nyall dot dawson at gmail dot com
***************************************************************************/
/***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/
#ifndef QGSPROCESSINGWIDGETWRAPPER_H
#define QGSPROCESSINGWIDGETWRAPPER_H
#include <QObject>
#include <QWidget>
#include "qgis_gui.h"
#include "qgis_sip.h"
#include <QPointer>
class QgsProcessingParameterDefinition;
class QgsProcessingContext;
class QLabel;
/**
* \class QgsAbstractProcessingParameterWidgetWrapper
*
* 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().
*
* \ingroup gui
* \since QGIS 3.4
*/
class GUI_EXPORT QgsAbstractProcessingParameterWidgetWrapper : public QObject
{
Q_OBJECT
public:
//! Types of dialogs the widgets can be created for
enum WidgetType
{
Standard, //!< Standard algorithm dialog
Batch, //!< Batch processing dialog
Modeler, //!< Modeler dialog
};
/**
* Constructor for QgsAbstractProcessingParameterWidgetWrapper, for the specified
* \a parameter definition and dialog \a type.
*/
QgsAbstractProcessingParameterWidgetWrapper( const QgsProcessingParameterDefinition *parameter = nullptr,
WidgetType type = Standard, QObject *parent SIP_TRANSFERTHIS = nullptr );
/**
* Returns the dialog type for which widgets and labels will be created by this wrapper.
*/
WidgetType type() const;
/**
* 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 \a context is used while initializing the widget to the parameter's default value.
*
* \see createWrappedLabel()
*/
QWidget *createWrappedWidget( const QgsProcessingContext &context ) SIP_FACTORY;
/**
* 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 nullptr for this method. If nullptr 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().
*
* \see createWrappedWidget()
*/
QLabel *createWrappedLabel() SIP_FACTORY;
/**
* Returns the current wrapped widget, if any.
* \see createWrappedWidget()
*/
QWidget *wrappedWidget();
/**
* Returns the current wrapped label, if any.
* \see createWrappedLabel()
*/
QLabel *wrappedLabel();
/**
* Returns the parameter definition associated with this wrapper.
*/
const QgsProcessingParameterDefinition *parameterDefinition() const;
/**
* Sets the current \a value for the parameter.
*
* The \a context argument is used to specify the wider Processing context which the
* current value is associated with.
*
* \see value()
*/
virtual void setWidgetValue( const QVariant &value, const QgsProcessingContext &context ) = 0;
/**
* Returns the current value of the parameter.
*
* \see setWidgetValue()
*/
virtual QVariant value() const = 0;
/**
* 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.
*/
virtual void postInitialize( const QList< QgsAbstractProcessingParameterWidgetWrapper * > &wrappers );
protected:
/**
* Creates a new widget which allows customization of the parameter's value.
*
* The caller takes ownership of the returned widget.
*
* \see createLabel()
*/
virtual QWidget *createWidget() = 0 SIP_FACTORY;
/**
* 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 nullptr for this method. If nullptr is returned, then no
* label should be shown for the parameter's widget (i.e. the label is embedded inside the
* widget itself).
*
* \see createWidget()
*/
virtual QLabel *createLabel() SIP_FACTORY;
private:
WidgetType mType = Standard;
const QgsProcessingParameterDefinition *mParameterDefinition = nullptr;
QPointer< QWidget > mWidget;
QPointer< QLabel > mLabel;
};
/**
* \class QgsProcessingParameterWidgetFactoryInterface
*
* An interface for Processing widget wrapper factories.
*
* Widget wrapper factories allow creation of QgsAbstractProcessingParameterWidgetWrapper objects.
* They are centrally managed by QgsProcessingGuiRegistry. Usually, individual factories
* are not directly utilized, rather the QgsGui::processingGuiRegistry()->createParameterWidgetWrapper()
* method is used to create widget wrappers.
*
* \ingroup gui
* \since QGIS 3.4
*/
class GUI_EXPORT QgsProcessingParameterWidgetFactoryInterface
{
public:
virtual ~QgsProcessingParameterWidgetFactoryInterface() = default;
/**
* Returns the type string for the parameter type the factory is associated with.
*/
virtual QString parameterType() const = 0;
/**
* Creates a new widget wrapper for the specified \a parameter definition.
*
* The \a type argument indicates the dialog type to create a wrapper for.
*/
virtual QgsAbstractProcessingParameterWidgetWrapper *createWidgetWrapper( const QgsProcessingParameterDefinition *parameter,
QgsAbstractProcessingParameterWidgetWrapper::WidgetType type ) = 0 SIP_FACTORY;
};
#endif // QGSPROCESSINGWIDGETWRAPPER_H

View File

@ -0,0 +1,117 @@
/***************************************************************************
qgsprocessingwidgetwrapperimpl.cpp
---------------------
begin : August 2018
copyright : (C) 2018 by Nyall Dawson
email : nyall dot dawson at gmail dot com
***************************************************************************/
/***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/
#include "qgsprocessingwidgetwrapperimpl.h"
#include "qgsprocessingparameters.h"
#include <QLabel>
#include <QHBoxLayout>
#include <QCheckBox>
#include <QComboBox>
///@cond PRIVATE
//
// QgsProcessingBooleanWidgetWrapper
//
QgsProcessingBooleanWidgetWrapper::QgsProcessingBooleanWidgetWrapper( const QgsProcessingParameterDefinition *parameter, WidgetType type, QWidget *parent )
: QgsAbstractProcessingParameterWidgetWrapper( parameter, type, parent )
{
}
QWidget *QgsProcessingBooleanWidgetWrapper::createWidget()
{
switch ( type() )
{
case Standard:
{
QString description = parameterDefinition()->description();
if ( parameterDefinition()->flags() & QgsProcessingParameterDefinition::FlagOptional )
description = QObject::tr( "%1 [optional]" ).arg( description );
mCheckBox = new QCheckBox( description );
mCheckBox->setToolTip( parameterDefinition()->toolTip() );
return mCheckBox;
};
case Batch:
{
mComboBox = new QComboBox();
mComboBox->addItem( tr( "Yes" ), true );
mComboBox->addItem( tr( "No" ), false );
mComboBox->setToolTip( parameterDefinition()->toolTip() );
return mComboBox;
}
}
return nullptr;
}
QLabel *QgsProcessingBooleanWidgetWrapper::createLabel()
{
// avoid creating labels in standard dialogs
if ( type() == QgsAbstractProcessingParameterWidgetWrapper::Standard )
return nullptr;
else
return QgsAbstractProcessingParameterWidgetWrapper::createLabel();
}
void QgsProcessingBooleanWidgetWrapper::setWidgetValue( const QVariant &value, const QgsProcessingContext &context )
{
switch ( type() )
{
case Standard:
{
const bool v = QgsProcessingParameters::parameterAsBool( parameterDefinition(), value, context );
mCheckBox->setChecked( v );
break;
}
case Batch:
{
const bool v = QgsProcessingParameters::parameterAsBool( parameterDefinition(), value, context );
mComboBox->setCurrentIndex( mComboBox->findData( v ) );
break;
}
}
}
QVariant QgsProcessingBooleanWidgetWrapper::value() const
{
switch ( type() )
{
case Standard:
return mCheckBox->isChecked();
case Batch:
return mComboBox->currentData();
}
}
QString QgsProcessingBooleanWidgetWrapper::parameterType() const
{
return QStringLiteral( "boolean" );
}
QgsAbstractProcessingParameterWidgetWrapper *QgsProcessingBooleanWidgetWrapper::createWidgetWrapper( const QgsProcessingParameterDefinition *parameter, QgsAbstractProcessingParameterWidgetWrapper::WidgetType type )
{
return new QgsProcessingBooleanWidgetWrapper( parameter, type );
}
///@endcond PRIVATE

View File

@ -0,0 +1,55 @@
/***************************************************************************
qgsprocessingwidgetwrapperimpl.h
---------------------
begin : August 2018
copyright : (C) 2018 by Nyall Dawson
email : nyall dot dawson at gmail dot com
***************************************************************************/
/***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/
#ifndef QGSPROCESSINGWIDGETWRAPPERIMPL_H
#define QGSPROCESSINGWIDGETWRAPPERIMPL_H
#define SIP_NO_FILE
#include "qgsprocessingwidgetwrapper.h"
class QCheckBox;
class QComboBox;
///@cond PRIVATE
class GUI_EXPORT QgsProcessingBooleanWidgetWrapper : public QgsAbstractProcessingParameterWidgetWrapper, public QgsProcessingParameterWidgetFactoryInterface
{
public:
QgsProcessingBooleanWidgetWrapper( const QgsProcessingParameterDefinition *parameter = nullptr,
QgsAbstractProcessingParameterWidgetWrapper::WidgetType type = Standard, QWidget *parent = nullptr );
// QgsProcessingParameterWidgetFactoryInterface
QString parameterType() const override;
QgsAbstractProcessingParameterWidgetWrapper *createWidgetWrapper( const QgsProcessingParameterDefinition *parameter, QgsAbstractProcessingParameterWidgetWrapper::WidgetType type ) override;
// QgsProcessingParameterWidgetWrapper interface
QWidget *createWidget() override SIP_FACTORY;
QLabel *createLabel() override SIP_FACTORY;
void setWidgetValue( const QVariant &value, const QgsProcessingContext &context ) override;
QVariant value() const override;
private:
QCheckBox *mCheckBox = nullptr;
QComboBox *mComboBox = nullptr;
};
///@endcond PRIVATE
#endif // QGSPROCESSINGWIDGETWRAPPERIMPL_H

View File

@ -0,0 +1,52 @@
/***************************************************************************
qgsprocessingwidgetwrapperimpl.h
---------------------
begin : August 2018
copyright : (C) 2018 by Nyall Dawson
email : nyall dot dawson at gmail dot com
***************************************************************************/
/***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/
#ifndef QGSPROCESSINGWIDGETWRAPPERIMPL_H
#define QGSPROCESSINGWIDGETWRAPPERIMPL_H
#define SIP_NO_FILE
#include "qgsprocessingwidgetwrapper.h"
class QCheckBox;
class QgsProcessingBooleanWidgetWrapper : public QgsProcessingParameterWidgetWrapper
{
public:
QgsProcessingBooleanWidgetWrapper( const QgsProcessingParameterDefinition *parameter, QgsProcessingParameterWidgetWrapper::WidgetType type, QWidget *parent = nullptr );
void setValue( const QVariant &value ) override;
QVariant value() const override;
private:
QCheckBox *mCheckBox = nullptr;
};
class QgsProcessingBooleanWidgetFactory : public QgsProcessingParameterWidgetFactory
{
public:
QString parameterType() const override;
QgsProcessingParameterWidgetWrapper *createWidget( const QgsProcessingParameterDefinition *parameter, QgsProcessingParameterWidgetWrapper::WidgetType type ) override;
QLabel *createLabel( const QgsProcessingParameterDefinition *parameter, QgsProcessingParameterWidgetWrapper::WidgetType type ) override;
};
#endif // QGSPROCESSINGWIDGETWRAPPERIMPL_H

View File

@ -16,14 +16,97 @@
***************************************************************************/
#include <QObject>
#include <QLabel>
#include <QCheckBox>
#include <QComboBox>
#include "qgstest.h"
#include "qgsgui.h"
#include "qgsprocessingguiregistry.h"
#include "qgsprocessingregistry.h"
#include "qgsprocessingalgorithmconfigurationwidget.h"
#include "qgsprocessingwidgetwrapper.h"
#include "qgsprocessingwidgetwrapperimpl.h"
#include "qgsnativealgorithms.h"
#include "qgsxmlutils.h"
class TestParamType : public QgsProcessingParameterDefinition
{
public:
TestParamType( const QString &type, const QString &name, const QVariant &defaultValue = QVariant() )
: QgsProcessingParameterDefinition( name, name, defaultValue )
, mType( type )
{}
QString mType;
QgsProcessingParameterDefinition *clone() const override
{
return new TestParamType( mType, name() );
}
QString type() const override { return mType; }
QString valueAsPythonString( const QVariant &, QgsProcessingContext & ) const override { return QString(); }
QString asScriptCode() const override { return QString(); }
};
class TestWidgetWrapper : public QgsAbstractProcessingParameterWidgetWrapper
{
public:
TestWidgetWrapper( const QgsProcessingParameterDefinition *parameter = nullptr,
WidgetType type = Standard )
: QgsAbstractProcessingParameterWidgetWrapper( parameter, type )
{}
QWidget *createWidget() override
{
return nullptr;
}
QLabel *createLabel() override
{
return nullptr;
}
void setWidgetValue( const QVariant &, const QgsProcessingContext & ) override
{
}
QVariant value() const override
{
return QVariant();
}
};
class TestWidgetFactory : public QgsProcessingParameterWidgetFactoryInterface
{
public:
TestWidgetFactory( const QString &type )
: type( type )
{}
QString type;
QString parameterType() const override
{
return type;
}
QgsAbstractProcessingParameterWidgetWrapper *createWidgetWrapper( const QgsProcessingParameterDefinition *parameter,
QgsAbstractProcessingParameterWidgetWrapper::WidgetType type ) override
{
return new TestWidgetWrapper( parameter, type );
}
};
class TestProcessingGui : public QObject
{
Q_OBJECT
@ -37,6 +120,9 @@ class TestProcessingGui : public QObject
void cleanup();// will be called after every testfunction.
void testSetGetConfig();
void testFilterAlgorithmConfig();
void testWrapperFactoryRegistry();
void testWrapperGeneral();
void testBooleanWrapper();
};
void TestProcessingGui::initTestCase()
@ -110,5 +196,122 @@ void TestProcessingGui::testFilterAlgorithmConfig()
QCOMPARE( configDoc.toString(), configControlDoc.toString() );
}
void TestProcessingGui::testWrapperFactoryRegistry()
{
QgsProcessingGuiRegistry registry;
TestParamType numParam( QStringLiteral( "num" ), QStringLiteral( "num" ) );
TestParamType stringParam( QStringLiteral( "str" ), QStringLiteral( "str" ) );
QVERIFY( !registry.createParameterWidgetWrapper( &numParam, QgsAbstractProcessingParameterWidgetWrapper::Standard ) );
TestWidgetFactory *factory = new TestWidgetFactory( QStringLiteral( "str" ) );
QVERIFY( registry.addParameterWidgetFactory( factory ) );
// duplicate type not allowed
TestWidgetFactory *factory2 = new TestWidgetFactory( QStringLiteral( "str" ) );
QVERIFY( !registry.addParameterWidgetFactory( factory2 ) );
delete factory2;
QgsAbstractProcessingParameterWidgetWrapper *wrapper = registry.createParameterWidgetWrapper( &numParam, QgsAbstractProcessingParameterWidgetWrapper::Standard );
QVERIFY( !wrapper );
wrapper = registry.createParameterWidgetWrapper( &stringParam, QgsAbstractProcessingParameterWidgetWrapper::Standard );
QVERIFY( wrapper );
QCOMPARE( wrapper->parameterDefinition()->type(), QStringLiteral( "str" ) );
delete wrapper;
TestWidgetFactory *factory3 = new TestWidgetFactory( QStringLiteral( "num" ) );
QVERIFY( registry.addParameterWidgetFactory( factory3 ) );
wrapper = registry.createParameterWidgetWrapper( &numParam, QgsAbstractProcessingParameterWidgetWrapper::Standard );
QVERIFY( wrapper );
QCOMPARE( wrapper->parameterDefinition()->type(), QStringLiteral( "num" ) );
delete wrapper;
// removing
registry.removeParameterWidgetFactory( nullptr );
TestWidgetFactory *factory4 = new TestWidgetFactory( QStringLiteral( "xxxx" ) );
registry.removeParameterWidgetFactory( factory4 );
registry.removeParameterWidgetFactory( factory );
wrapper = registry.createParameterWidgetWrapper( &stringParam, QgsAbstractProcessingParameterWidgetWrapper::Standard );
QVERIFY( !wrapper );
wrapper = registry.createParameterWidgetWrapper( &numParam, QgsAbstractProcessingParameterWidgetWrapper::Standard );
QVERIFY( wrapper );
QCOMPARE( wrapper->parameterDefinition()->type(), QStringLiteral( "num" ) );
delete wrapper;
}
void TestProcessingGui::testWrapperGeneral()
{
TestParamType param( QStringLiteral( "boolean" ), QStringLiteral( "bool" ) );
QgsProcessingBooleanWidgetWrapper wrapper( &param );
QCOMPARE( wrapper.type(), QgsAbstractProcessingParameterWidgetWrapper::Standard );
QgsProcessingBooleanWidgetWrapper wrapper2( &param, QgsAbstractProcessingParameterWidgetWrapper::Batch );
QCOMPARE( wrapper2.type(), QgsAbstractProcessingParameterWidgetWrapper::Batch );
QCOMPARE( wrapper2.parameterDefinition()->name(), QStringLiteral( "bool" ) );
QgsProcessingContext context;
QVERIFY( !wrapper2.wrappedWidget() );
QWidget *w = wrapper2.createWrappedWidget( context );
QVERIFY( w );
QCOMPARE( wrapper2.wrappedWidget(), w );
delete w;
QVERIFY( !wrapper2.wrappedLabel() );
QLabel *l = wrapper2.createWrappedLabel();
QCOMPARE( wrapper2.wrappedLabel(), l );
delete l;
// check that created widget starts with default value
param = TestParamType( QStringLiteral( "boolean" ), QStringLiteral( "bool" ), true );
QgsProcessingBooleanWidgetWrapper trueDefault( &param );
w = trueDefault.createWrappedWidget( context );
QVERIFY( trueDefault.value().toBool() );
delete w;
param = TestParamType( QStringLiteral( "boolean" ), QStringLiteral( "bool" ), false );
QgsProcessingBooleanWidgetWrapper falseDefault( &param );
w = falseDefault.createWrappedWidget( context );
QVERIFY( !falseDefault.value().toBool() );
delete w;
}
void TestProcessingGui::testBooleanWrapper()
{
TestParamType param( QStringLiteral( "boolean" ), QStringLiteral( "bool" ) );
// standard wrapper
QgsProcessingBooleanWidgetWrapper wrapper( &param );
QgsProcessingContext context;
QWidget *w = wrapper.createWrappedWidget( context );
wrapper.setWidgetValue( true, context );
QVERIFY( wrapper.value().toBool() );
QVERIFY( static_cast< QCheckBox * >( wrapper.wrappedWidget() )->isChecked() );
wrapper.setWidgetValue( false, context );
QVERIFY( !wrapper.value().toBool() );
QVERIFY( !static_cast< QCheckBox * >( wrapper.wrappedWidget() )->isChecked() );
// should be no label in standard mode
QVERIFY( !wrapper.createWrappedLabel() );
QCOMPARE( static_cast< QCheckBox * >( wrapper.wrappedWidget() )->text(), QStringLiteral( "bool" ) );
delete w;
// batch wrapper
QgsProcessingBooleanWidgetWrapper wrapperB( &param, QgsAbstractProcessingParameterWidgetWrapper::Batch );
w = wrapperB.createWrappedWidget( context );
wrapperB.setWidgetValue( true, context );
QVERIFY( wrapperB.value().toBool() );
QVERIFY( static_cast< QComboBox * >( wrapperB.wrappedWidget() )->currentData().toBool() );
wrapperB.setWidgetValue( false, context );
QVERIFY( !wrapperB.value().toBool() );
QVERIFY( !static_cast< QComboBox * >( wrapperB.wrappedWidget() )->currentData().toBool() );
// should be no label in batch mode
QVERIFY( !wrapperB.createWrappedLabel() );
delete w;
}
QGSTEST_MAIN( TestProcessingGui )
#include "testprocessinggui.moc"