Skip to content

Commit 49bfe69

Browse files
committed
[processing] add parameter representing raster band
1 parent 3c58599 commit 49bfe69

File tree

5 files changed

+318
-3
lines changed

5 files changed

+318
-3
lines changed

python/core/processing/qgsprocessingparameters.sip

Lines changed: 62 additions & 0 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

@@ -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 parentLayerParameter() const;
2010+
%Docstring
2011+
Returns the name of the parent layer parameter, or an empty string if this is not set.
2012+
.. seealso:: setParentLayerParameter()
2013+
:rtype: str
2014+
%End
2015+
2016+
void setParentLayerParameter( const QString &parentLayerParameter );
2017+
%Docstring
2018+
Sets the name of the parent layer parameter. Use an empty string if this is not required.
2019+
.. seealso:: parentLayerParameter()
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/gui/wrappers.py

Lines changed: 71 additions & 0 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
@@ -1257,6 +1259,72 @@ 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+
print(self.param)
1286+
print(self.param.parentLayerParameter)
1287+
for wrapper in wrappers:
1288+
if wrapper.param.name() == self.param.parentLayerParameter():
1289+
if self.dialogType in (DIALOG_STANDARD, DIALOG_BATCH):
1290+
self.setLayer(wrapper.value())
1291+
wrapper.widgetValueHasChanged.connect(self.parentValueChanged)
1292+
break
1293+
1294+
def parentValueChanged(self, wrapper):
1295+
self.setLayer(wrapper.value())
1296+
1297+
def setLayer(self, layer):
1298+
context = dataobjects.createContext()
1299+
if isinstance(layer, QgsProcessingParameterRasterLayer):
1300+
layer, ok = layer.source.valueAsString(context.expressionContext())
1301+
if isinstance(layer, str):
1302+
layer = QgsProcessingUtils.mapLayerFromString(layer, context)
1303+
self._layer = layer
1304+
self.refreshItems()
1305+
1306+
def refreshItems(self):
1307+
self.widget.setLayer(self._layer)
1308+
self.widget.setCurrentIndex(0)
1309+
1310+
def setValue(self, value):
1311+
if self.dialogType in (DIALOG_STANDARD, DIALOG_BATCH):
1312+
self.widget.setBand(value)
1313+
else:
1314+
self.setComboValue(value)
1315+
1316+
def value(self):
1317+
if self.dialogType in (DIALOG_STANDARD, DIALOG_BATCH):
1318+
f = self.widget.currentBand()
1319+
if self.param.flags() & QgsProcessingParameterDefinition.FlagOptional and not f:
1320+
return None
1321+
return f
1322+
else:
1323+
def validator(v):
1324+
return bool(v) or self.param.flags() & QgsProcessingParameterDefinition.FlagOptional
1325+
return self.comboValue(validator)
1326+
1327+
12601328
class WidgetWrapperFactory:
12611329

12621330
"""
@@ -1292,6 +1360,7 @@ def create_wrapper_from_metadata(param, dialog, row=0, col=0):
12921360

12931361
@staticmethod
12941362
def create_wrapper_from_class(param, dialog, row=0, col=0):
1363+
print("PARAM", param, param.name(), param.type())
12951364
wrapper = None
12961365
if param.type() == 'boolean':
12971366
wrapper = BooleanWidgetWrapper
@@ -1321,6 +1390,8 @@ def create_wrapper_from_class(param, dialog, row=0, col=0):
13211390
wrapper = TableFieldWidgetWrapper
13221391
elif param.type() == 'source':
13231392
wrapper = VectorWidgetWrapper
1393+
elif param.type() == 'band':
1394+
wrapper = BandWidgetWrapper
13241395
else:
13251396
assert False, param.type()
13261397
return wrapper(param, dialog, row, col)

python/plugins/processing/modeler/ModelerParameterDefinitionDialog.py

Lines changed: 27 additions & 3 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):
@@ -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.parentLayerParameter() == 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')))
@@ -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(

src/core/processing/qgsprocessingparameters.cpp

Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -787,6 +787,8 @@ QgsProcessingParameterDefinition *QgsProcessingParameters::parameterFromVariantM
787787
def.reset( new QgsProcessingParameterFileDestination( name ) );
788788
else if ( type == QgsProcessingParameterFolderDestination::typeName() )
789789
def.reset( new QgsProcessingParameterFolderDestination( name ) );
790+
else if ( type == QgsProcessingParameterBand::typeName() )
791+
def.reset( new QgsProcessingParameterBand( name ) );
790792

791793
if ( !def )
792794
return nullptr;
@@ -859,6 +861,8 @@ QgsProcessingParameterDefinition *QgsProcessingParameters::parameterFromScriptCo
859861
return QgsProcessingParameterFileDestination::fromScriptCode( name, description, isOptional, definition );
860862
else if ( type == QStringLiteral( "folderdestination" ) )
861863
return QgsProcessingParameterFolderDestination::fromScriptCode( name, description, isOptional, definition );
864+
else if ( type == QStringLiteral( "band" ) )
865+
return QgsProcessingParameterBand::fromScriptCode( name, description, isOptional, definition );
862866

863867
return nullptr;
864868
}
@@ -3145,3 +3149,103 @@ QgsProcessingParameterVectorDestination *QgsProcessingParameterVectorDestination
31453149

31463150
return new QgsProcessingParameterVectorDestination( name, description, type, definition, isOptional );
31473151
}
3152+
3153+
QgsProcessingParameterBand::QgsProcessingParameterBand( const QString &name, const QString &description, const QVariant &defaultValue, const QString &parentLayerParameterName, bool optional )
3154+
: QgsProcessingParameterDefinition( name, description, defaultValue, optional )
3155+
, mParentLayerParameter( parentLayerParameterName )
3156+
{
3157+
3158+
}
3159+
3160+
bool QgsProcessingParameterBand::checkValueIsAcceptable( const QVariant &input, QgsProcessingContext * ) const
3161+
{
3162+
if ( !input.isValid() )
3163+
return mFlags & FlagOptional;
3164+
3165+
if ( input.canConvert<QgsProperty>() )
3166+
{
3167+
return true;
3168+
}
3169+
3170+
bool ok = false;
3171+
double res = input.toInt( &ok );
3172+
Q_UNUSED( res );
3173+
if ( !ok )
3174+
return mFlags & FlagOptional;
3175+
3176+
return true;
3177+
}
3178+
3179+
QString QgsProcessingParameterBand::valueAsPythonString( const QVariant &value, QgsProcessingContext & ) const
3180+
{
3181+
if ( value.canConvert<QgsProperty>() )
3182+
return QStringLiteral( "QgsProperty.fromExpression('%1')" ).arg( value.value< QgsProperty >().asExpression() );
3183+
3184+
return value.toString();
3185+
}
3186+
3187+
QString QgsProcessingParameterBand::asScriptCode() const
3188+
{
3189+
QString code = QStringLiteral( "##%1=" ).arg( mName );
3190+
if ( mFlags & FlagOptional )
3191+
code += QStringLiteral( "optional " );
3192+
code += QStringLiteral( "band " );
3193+
3194+
code += mParentLayerParameter + ' ';
3195+
3196+
code += mDefault.toString();
3197+
return code.trimmed();
3198+
}
3199+
3200+
QStringList QgsProcessingParameterBand::dependsOnOtherParameters() const
3201+
{
3202+
QStringList depends;
3203+
if ( !mParentLayerParameter.isEmpty() )
3204+
depends << mParentLayerParameter;
3205+
return depends;
3206+
}
3207+
3208+
QString QgsProcessingParameterBand::parentLayerParameter() const
3209+
{
3210+
return mParentLayerParameter;
3211+
}
3212+
3213+
void QgsProcessingParameterBand::setParentLayerParameter( const QString &parentLayerParameter )
3214+
{
3215+
mParentLayerParameter = parentLayerParameter;
3216+
}
3217+
3218+
QVariantMap QgsProcessingParameterBand::toVariantMap() const
3219+
{
3220+
QVariantMap map = QgsProcessingParameterDefinition::toVariantMap();
3221+
map.insert( QStringLiteral( "parent_layer" ), mParentLayerParameter );
3222+
return map;
3223+
}
3224+
3225+
bool QgsProcessingParameterBand::fromVariantMap( const QVariantMap &map )
3226+
{
3227+
QgsProcessingParameterDefinition::fromVariantMap( map );
3228+
mParentLayerParameter = map.value( QStringLiteral( "parent_layer" ) ).toString();
3229+
return true;
3230+
}
3231+
3232+
QgsProcessingParameterBand *QgsProcessingParameterBand::fromScriptCode( const QString &name, const QString &description, bool isOptional, const QString &definition )
3233+
{
3234+
QString parent;
3235+
QString def = definition;
3236+
3237+
QRegularExpression re( "(.*?)\\s+(.*)$" );
3238+
QRegularExpressionMatch m = re.match( def );
3239+
if ( m.hasMatch() )
3240+
{
3241+
parent = m.captured( 1 ).trimmed();
3242+
def = m.captured( 2 );
3243+
}
3244+
else
3245+
{
3246+
parent = def;
3247+
def.clear();
3248+
}
3249+
3250+
return new QgsProcessingParameterBand( name, description, def.isEmpty() ? QVariant() : def, parent, isOptional );
3251+
}

0 commit comments

Comments
 (0)