Skip to content

Commit f964428

Browse files
authored
Merge pull request #4954 from alexbruy/processing-band
[FEATURE][needs-docs][processing] add parameter representing raster band
2 parents b20f4c6 + 9fe326d commit f964428

File tree

9 files changed

+452
-67
lines changed

9 files changed

+452
-67
lines changed

python/core/processing/qgsprocessingparameters.sip

Lines changed: 70 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -189,6 +189,8 @@ class QgsProcessingParameterDefinition
189189
sipType = sipType_QgsProcessingParameterFileDestination;
190190
else if ( sipCpp->type() == QgsProcessingParameterFolderDestination::typeName() )
191191
sipType = sipType_QgsProcessingParameterFolderDestination;
192+
else if ( sipCpp->type() == QgsProcessingParameterBand::typeName() )
193+
sipType = sipType_QgsProcessingParameterBand;
192194
%End
193195
public:
194196

@@ -1356,17 +1358,17 @@ class QgsProcessingParameterExpression : QgsProcessingParameterDefinition
13561358
virtual QStringList dependsOnOtherParameters() const;
13571359

13581360

1359-
QString parentLayerParameter() const;
1361+
QString parentLayerParameterName() const;
13601362
%Docstring
13611363
Returns the name of the parent layer parameter, or an empty string if this is not set.
1362-
.. seealso:: setParentLayerParameter()
1364+
.. seealso:: setParentLayerParameterName()
13631365
:rtype: str
13641366
%End
13651367

1366-
void setParentLayerParameter( const QString &parentLayerParameter );
1368+
void setParentLayerParameterName( const QString &parentLayerParameterName );
13671369
%Docstring
13681370
Sets the name of the parent layer parameter. Use an empty string if this is not required.
1369-
.. seealso:: parentLayerParameter()
1371+
.. seealso:: parentLayerParameterName()
13701372
%End
13711373

13721374
virtual QVariantMap toVariantMap() const;
@@ -1485,17 +1487,17 @@ class QgsProcessingParameterField : QgsProcessingParameterDefinition
14851487
virtual QStringList dependsOnOtherParameters() const;
14861488

14871489

1488-
QString parentLayerParameter() const;
1490+
QString parentLayerParameterName() const;
14891491
%Docstring
14901492
Returns the name of the parent layer parameter, or an empty string if this is not set.
1491-
.. seealso:: setParentLayerParameter()
1493+
.. seealso:: setParentLayerParameterName()
14921494
:rtype: str
14931495
%End
14941496

1495-
void setParentLayerParameter( const QString &parentLayerParameter );
1497+
void setParentLayerParameterName( const QString &parentLayerParameterName );
14961498
%Docstring
14971499
Sets the name of the parent layer parameter. Use an empty string if this is not required.
1498-
.. seealso:: parentLayerParameter()
1500+
.. seealso:: parentLayerParameterName()
14991501
%End
15001502

15011503
DataType dataType() const;
@@ -1970,6 +1972,66 @@ class QgsProcessingParameterFolderDestination : QgsProcessingDestinationParamete
19701972

19711973
};
19721974

1975+
class QgsProcessingParameterBand : QgsProcessingParameterDefinition
1976+
{
1977+
%Docstring
1978+
A raster band parameter for Processing algorithms.
1979+
.. versionadded:: 3.0
1980+
%End
1981+
1982+
%TypeHeaderCode
1983+
#include "qgsprocessingparameters.h"
1984+
%End
1985+
public:
1986+
1987+
QgsProcessingParameterBand( const QString &name, const QString &description = QString(), const QVariant &defaultValue = QVariant(),
1988+
const QString &parentLayerParameterName = QString(),
1989+
bool optional = false );
1990+
%Docstring
1991+
Constructor for QgsProcessingParameterBand.
1992+
%End
1993+
1994+
static QString typeName();
1995+
%Docstring
1996+
Returns the type name for the parameter class.
1997+
:rtype: str
1998+
%End
1999+
virtual QString type() const;
2000+
virtual bool checkValueIsAcceptable( const QVariant &input, QgsProcessingContext *context = 0 ) const;
2001+
2002+
virtual QString valueAsPythonString( const QVariant &value, QgsProcessingContext &context ) const;
2003+
2004+
virtual QString asScriptCode() const;
2005+
2006+
virtual QStringList dependsOnOtherParameters() const;
2007+
2008+
2009+
QString parentLayerParameterName() const;
2010+
%Docstring
2011+
Returns the name of the parent layer parameter, or an empty string if this is not set.
2012+
.. seealso:: setParentLayerParameterName()
2013+
:rtype: str
2014+
%End
2015+
2016+
void setParentLayerParameterName( const QString &parentLayerParameterName );
2017+
%Docstring
2018+
Sets the name of the parent layer parameter. Use an empty string if this is not required.
2019+
.. seealso:: parentLayerParameterName()
2020+
%End
2021+
2022+
virtual QVariantMap toVariantMap() const;
2023+
2024+
virtual bool fromVariantMap( const QVariantMap &map );
2025+
2026+
2027+
static QgsProcessingParameterBand *fromScriptCode( const QString &name, const QString &description, bool isOptional, const QString &definition ) /Factory/;
2028+
%Docstring
2029+
Creates a new parameter using the definition from a script code.
2030+
:rtype: QgsProcessingParameterBand
2031+
%End
2032+
2033+
};
2034+
19732035

19742036

19752037
/************************************************************************

python/plugins/processing/algs/qgis/RasterLayerStatistics.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@
3030
from qgis.core import (QgsRectangle,
3131
QgsRasterBandStats,
3232
QgsProcessingParameterRasterLayer,
33-
QgsProcessingParameterNumber,
33+
QgsProcessingParameterBand,
3434
QgsProcessingParameterFileDestination,
3535
QgsProcessingOutputHtml,
3636
QgsProcessingOutputNumber)
@@ -60,10 +60,10 @@ def __init__(self):
6060
def initAlgorithm(self, config=None):
6161
self.addParameter(QgsProcessingParameterRasterLayer(self.INPUT,
6262
self.tr('Input layer')))
63-
self.addParameter(QgsProcessingParameterNumber(self.BAND,
64-
self.tr('Band number'),
65-
QgsProcessingParameterNumber.Integer,
66-
1, False, 1, 999))
63+
self.addParameter(QgsProcessingParameterBand(self.BAND,
64+
self.tr('Band number'),
65+
1,
66+
self.INPUT))
6767
self.addParameter(QgsProcessingParameterFileDestination(self.OUTPUT_HTML_FILE, self.tr('Statistics'), self.tr('HTML files (*.html)'), None, True))
6868
self.addOutput(QgsProcessingOutputHtml(self.OUTPUT_HTML_FILE, self.tr('Statistics')))
6969

python/plugins/processing/algs/qgis/ZonalStatistics.py

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@
3535
QgsProcessingParameterVectorLayer,
3636
QgsProcessingParameterRasterLayer,
3737
QgsProcessingParameterString,
38-
QgsProcessingParameterNumber,
38+
QgsProcessingParameterBand,
3939
QgsProcessingParameterEnum,
4040
QgsProcessingOutputVectorLayer)
4141

@@ -78,9 +78,10 @@ def initAlgorithm(self, config=None):
7878

7979
self.addParameter(QgsProcessingParameterRasterLayer(self.INPUT_RASTER,
8080
self.tr('Raster layer')))
81-
self.addParameter(QgsProcessingParameterNumber(self.RASTER_BAND,
82-
self.tr('Raster band'),
83-
minValue=1, maxValue=999, defaultValue=1))
81+
self.addParameter(QgsProcessingParameterBand(self.RASTER_BAND,
82+
self.tr('Raster band'),
83+
1,
84+
self.INPUT_RASTER))
8485
self.addParameter(QgsProcessingParameterVectorLayer(self.INPUT_VECTOR,
8586
self.tr('Vector layer containing zones'),
8687
[QgsProcessing.TypeVectorPolygon]))

python/plugins/processing/gui/wrappers.py

Lines changed: 72 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@
6262
QgsProcessingParameterVectorLayer,
6363
QgsProcessingParameterField,
6464
QgsProcessingParameterFeatureSource,
65+
QgsProcessingParameterBand,
6566
QgsProcessingFeatureSourceDefinition,
6667
QgsProcessingOutputRasterLayer,
6768
QgsProcessingOutputVectorLayer,
@@ -89,6 +90,7 @@
8990
QgsProjectionSelectionDialog,
9091
QgsMapLayerComboBox,
9192
QgsProjectionSelectionWidget,
93+
QgsRasterBandComboBox,
9294
)
9395
from qgis.PyQt.QtCore import pyqtSignal, QObject, QVariant, Qt
9496
from qgis.utils import iface
@@ -990,7 +992,7 @@ class ExpressionWidgetWrapper(WidgetWrapper):
990992

991993
def createWidget(self):
992994
if self.dialogType in (DIALOG_STANDARD, DIALOG_BATCH):
993-
if self.param.parentLayerParameter():
995+
if self.param.parentLayerParameterName():
994996
widget = QgsFieldExpressionWidget()
995997
else:
996998
widget = QgsExpressionLineEdit()
@@ -1008,7 +1010,7 @@ def createWidget(self):
10081010

10091011
def postInitialize(self, wrappers):
10101012
for wrapper in wrappers:
1011-
if wrapper.param.name() == self.param.parentLayerParameter():
1013+
if wrapper.param.name() == self.param.parentLayerParameterName():
10121014
if self.dialogType in (DIALOG_STANDARD, DIALOG_BATCH):
10131015
self.setLayer(wrapper.value())
10141016
wrapper.widgetValueHasChanged.connect(self.parentLayerChanged)
@@ -1185,7 +1187,7 @@ def createWidget(self):
11851187

11861188
def postInitialize(self, wrappers):
11871189
for wrapper in wrappers:
1188-
if wrapper.param.name() == self.param.parentLayerParameter():
1190+
if wrapper.param.name() == self.param.parentLayerParameterName():
11891191
if self.dialogType in (DIALOG_STANDARD, DIALOG_BATCH):
11901192
self.setLayer(wrapper.value())
11911193
wrapper.widgetValueHasChanged.connect(self.parentValueChanged)
@@ -1257,6 +1259,70 @@ def validator(v):
12571259
return self.comboValue(validator)
12581260

12591261

1262+
class BandWidgetWrapper(WidgetWrapper):
1263+
1264+
NOT_SET = '[Not set]'
1265+
1266+
def createWidget(self):
1267+
self._layer = None
1268+
1269+
if self.dialogType in (DIALOG_STANDARD, DIALOG_BATCH):
1270+
widget = QgsRasterBandComboBox()
1271+
widget.setShowNotSetOption(self.param.flags() & QgsProcessingParameterDefinition.FlagOptional)
1272+
widget.bandChanged.connect(lambda: self.widgetValueHasChanged.emit(self))
1273+
return widget
1274+
else:
1275+
widget = QComboBox()
1276+
widget.setEditable(True)
1277+
fields = self.dialog.getAvailableValuesOfType([QgsProcessingParameterBand, QgsProcessingParameterNumber], [QgsProcessingOutputNumber])
1278+
if self.param.flags() & QgsProcessingParameterDefinition.FlagOptional:
1279+
widget.addItem(self.NOT_SET, None)
1280+
for f in fields:
1281+
widget.addItem(self.dialog.resolveValueDescription(f), f)
1282+
return widget
1283+
1284+
def postInitialize(self, wrappers):
1285+
for wrapper in wrappers:
1286+
if wrapper.param.name() == self.param.parentLayerParameterName():
1287+
if self.dialogType in (DIALOG_STANDARD, DIALOG_BATCH):
1288+
self.setLayer(wrapper.value())
1289+
wrapper.widgetValueHasChanged.connect(self.parentValueChanged)
1290+
break
1291+
1292+
def parentValueChanged(self, wrapper):
1293+
self.setLayer(wrapper.value())
1294+
1295+
def setLayer(self, layer):
1296+
context = dataobjects.createContext()
1297+
if isinstance(layer, QgsProcessingParameterRasterLayer):
1298+
layer, ok = layer.source.valueAsString(context.expressionContext())
1299+
if isinstance(layer, str):
1300+
layer = QgsProcessingUtils.mapLayerFromString(layer, context)
1301+
self._layer = layer
1302+
self.refreshItems()
1303+
1304+
def refreshItems(self):
1305+
self.widget.setLayer(self._layer)
1306+
self.widget.setCurrentIndex(0)
1307+
1308+
def setValue(self, value):
1309+
if self.dialogType in (DIALOG_STANDARD, DIALOG_BATCH):
1310+
self.widget.setBand(value)
1311+
else:
1312+
self.setComboValue(value)
1313+
1314+
def value(self):
1315+
if self.dialogType in (DIALOG_STANDARD, DIALOG_BATCH):
1316+
f = self.widget.currentBand()
1317+
if self.param.flags() & QgsProcessingParameterDefinition.FlagOptional and not f:
1318+
return None
1319+
return f
1320+
else:
1321+
def validator(v):
1322+
return bool(v) or self.param.flags() & QgsProcessingParameterDefinition.FlagOptional
1323+
return self.comboValue(validator)
1324+
1325+
12601326
class WidgetWrapperFactory:
12611327

12621328
"""
@@ -1292,6 +1358,7 @@ def create_wrapper_from_metadata(param, dialog, row=0, col=0):
12921358

12931359
@staticmethod
12941360
def create_wrapper_from_class(param, dialog, row=0, col=0):
1361+
print("PARAM", param, param.name(), param.type())
12951362
wrapper = None
12961363
if param.type() == 'boolean':
12971364
wrapper = BooleanWidgetWrapper
@@ -1321,6 +1388,8 @@ def create_wrapper_from_class(param, dialog, row=0, col=0):
13211388
wrapper = TableFieldWidgetWrapper
13221389
elif param.type() == 'source':
13231390
wrapper = VectorWidgetWrapper
1391+
elif param.type() == 'band':
1392+
wrapper = BandWidgetWrapper
13241393
else:
13251394
assert False, param.type()
13261395
return wrapper(param, dialog, row, col)

python/plugins/processing/modeler/ModelerParameterDefinitionDialog.py

Lines changed: 29 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,8 @@
4848
QgsProcessingParameterExpression,
4949
QgsProcessingParameterVectorLayer,
5050
QgsProcessingParameterField,
51-
QgsProcessingParameterFeatureSource)
51+
QgsProcessingParameterFeatureSource,
52+
QgsProcessingParameterBand)
5253
from qgis.PyQt.QtCore import (Qt,
5354
QByteArray)
5455
from qgis.PyQt.QtWidgets import (QDialog,
@@ -76,6 +77,7 @@ class ModelerParameterDefinitionDialog(QDialog):
7677
PARAMETER_POINT = 'Point'
7778
PARAMETER_CRS = 'CRS'
7879
PARAMETER_MULTIPLE = 'Multiple Input'
80+
PARAMETER_BAND = 'Raster band'
7981

8082
paramTypes = [
8183
PARAMETER_BOOLEAN,
@@ -90,7 +92,8 @@ class ModelerParameterDefinitionDialog(QDialog):
9092
PARAMETER_VECTOR,
9193
PARAMETER_POINT,
9294
PARAMETER_CRS,
93-
PARAMETER_MULTIPLE
95+
PARAMETER_MULTIPLE,
96+
PARAMETER_BAND
9497
]
9598

9699
def __init__(self, alg, paramType=None, param=None):
@@ -141,7 +144,7 @@ def setupUi(self):
141144
if isinstance(definition, (QgsProcessingParameterFeatureSource, QgsProcessingParameterVectorLayer)):
142145
self.parentCombo.addItem(definition.description(), definition.name())
143146
if self.param is not None:
144-
if self.param.parentLayerParameter() == definition.name():
147+
if self.param.parentLayerParameterName() == definition.name():
145148
self.parentCombo.setCurrentIndex(idx)
146149
idx += 1
147150
self.verticalLayout.addWidget(self.parentCombo)
@@ -167,7 +170,20 @@ def setupUi(self):
167170
if self.param is not None:
168171
self.multipleCheck.setChecked(self.param.allowMultiple())
169172
self.verticalLayout.addWidget(self.multipleCheck)
170-
173+
elif self.paramType == ModelerParameterDefinitionDialog.PARAMETER_BAND or \
174+
isinstance(self.param, QgsProcessingParameterBand):
175+
self.verticalLayout.addWidget(QLabel(self.tr('Parent layer')))
176+
self.parentCombo = QComboBox()
177+
idx = 0
178+
for param in list(self.alg.parameterComponents().values()):
179+
definition = self.alg.parameterDefinition(param.parameterName())
180+
if isinstance(definition, (QgsProcessingParameterRasterLayer)):
181+
self.parentCombo.addItem(definition.description(), definition.name())
182+
if self.param is not None:
183+
if self.param.parentLayerParameterName() == definition.name():
184+
self.parentCombo.setCurrentIndex(idx)
185+
idx += 1
186+
self.verticalLayout.addWidget(self.parentCombo)
171187
elif (self.paramType == ModelerParameterDefinitionDialog.PARAMETER_VECTOR or
172188
isinstance(self.param, QgsProcessingParameterFeatureSource)):
173189
self.verticalLayout.addWidget(QLabel(self.tr('Shape type')))
@@ -230,7 +246,7 @@ def setupUi(self):
230246
if isinstance(definition, (QgsProcessingParameterFeatureSource, QgsProcessingParameterVectorLayer)):
231247
self.parentCombo.addItem(definition.description(), definition.name())
232248
if self.param is not None:
233-
if self.param.parentLayerParameter() == definition.name():
249+
if self.param.parentLayerParameterName() == definition.name():
234250
self.parentCombo.setCurrentIndex(idx)
235251
idx += 1
236252
self.verticalLayout.addWidget(self.parentCombo)
@@ -318,6 +334,14 @@ def okPressed(self):
318334
parent = self.parentCombo.currentData()
319335
datatype = self.datatypeCombo.currentData()
320336
self.param = QgsProcessingParameterField(name, description, None, parent, datatype, self.multipleCheck.isChecked())
337+
elif (self.paramType == ModelerParameterDefinitionDialog.PARAMETER_BAND or
338+
isinstance(self.param, QgsProcessingParameterBand)):
339+
if self.parentCombo.currentIndex() < 0:
340+
QMessageBox.warning(self, self.tr('Unable to define parameter'),
341+
self.tr('Wrong or missing parameter values'))
342+
return
343+
parent = self.parentCombo.currentData()
344+
self.param = QgsProcessingParameterBand(name, description, None, parent)
321345
elif (self.paramType == ModelerParameterDefinitionDialog.PARAMETER_RASTER or
322346
isinstance(self.param, QgsProcessingParameterRasterLayer)):
323347
self.param = QgsProcessingParameterRasterLayer(

python/plugins/processing/modeler/ModelerScene.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -103,8 +103,8 @@ def paintModel(self, model, controls=True):
103103
for input_name in list(model.parameterComponents().keys()):
104104
idx = 0
105105
parameter_def = model.parameterDefinition(input_name)
106-
if hasattr(parameter_def, 'parentLayerParameter') and parameter_def.parentLayerParameter():
107-
parent_name = parameter_def.parentLayerParameter()
106+
if hasattr(parameter_def, 'parentLayerParameterName') and parameter_def.parentLayerParameterName():
107+
parent_name = parameter_def.parentLayerParameterName()
108108
if input_name in self.paramItems and parent_name in self.paramItems:
109109
input_item = self.paramItems[input_name]
110110
parent_item = self.paramItems[parent_name]

0 commit comments

Comments
 (0)