Skip to content

Commit

Permalink
[needs-docs][processing] Add a multiple selection mode to the band pa…
Browse files Browse the repository at this point in the history
…rameter
  • Loading branch information
nirvn committed Sep 4, 2018
1 parent 53e2e4e commit 13ecaa4
Show file tree
Hide file tree
Showing 5 changed files with 183 additions and 15 deletions.
Expand Up @@ -2473,7 +2473,8 @@ A raster band parameter for Processing algorithms.

QgsProcessingParameterBand( const QString &name, const QString &description = QString(), const QVariant &defaultValue = QVariant(),
const QString &parentLayerParameterName = QString(),
bool optional = false );
bool optional = false,
bool allowMultiple = false );
%Docstring
Constructor for QgsProcessingParameterBand.
%End
Expand Down Expand Up @@ -2516,6 +2517,24 @@ Sets the name of the parent layer parameter. Use an empty string if this is not
static QgsProcessingParameterBand *fromScriptCode( const QString &name, const QString &description, bool isOptional, const QString &definition ) /Factory/;
%Docstring
Creates a new parameter using the definition from a script code.
%End

bool allowMultiple() const;
%Docstring
Returns whether multiple band selections are permitted.

.. seealso:: :py:func:`setAllowMultiple`

.. versionadded:: 3.4
%End

void setAllowMultiple( bool allowMultiple );
%Docstring
Sets whether multiple band selections are permitted.

.. seealso:: :py:func:`allowMultiple`

.. versionadded:: 3.4
%End

};
Expand Down
51 changes: 45 additions & 6 deletions python/plugins/processing/gui/wrappers.py
Expand Up @@ -76,6 +76,7 @@
QgsProcessingOutputNumber,
QgsProcessingModelChildParameterSource,
QgsProcessingModelAlgorithm,
QgsRasterDataProvider,
NULL)

from qgis.PyQt.QtWidgets import (
Expand Down Expand Up @@ -1655,6 +1656,8 @@ def createWidget(self):
self._layer = None

if self.dialogType in (DIALOG_STANDARD, DIALOG_BATCH):
if self.param.allowMultiple():
return MultipleInputPanel(options=[])
widget = QgsRasterBandComboBox()
widget.setShowNotSetOption(self.param.flags() & QgsProcessingParameterDefinition.FlagOptional)
widget.bandChanged.connect(lambda: self.widgetValueHasChanged.emit(self))
Expand Down Expand Up @@ -1689,24 +1692,60 @@ def setLayer(self, layer):
self._layer = layer
self.refreshItems()

def getBands(self):
bands = []

if self._layer is not None:
provider = self._layer.dataProvider()
for band in range(1, provider.bandCount() + 1):
name = provider.generateBandName(band)
interpretation = provider.colorInterpretationName(band)
if interpretation != "Undefined":
name = name + ' ({})'.format(interpretation)
bands.append(name)
return bands

def refreshItems(self):
self.widget.setLayer(self._layer)
self.widget.setCurrentIndex(0)
if self.param.allowMultiple():
self.widget.setSelectedItems([])
self.widget.updateForOptions(self.getBands())
else:
self.widget.setLayer(self._layer)
self.widget.setCurrentIndex(0)

def setValue(self, value):
if value is None or value == NULL:
return

if self.dialogType in (DIALOG_STANDARD, DIALOG_BATCH):
self.widget.setBand(value)
if self.param.allowMultiple():
options = self.widget.options
selected = []
if isinstance(value, str):
value = value.split(';')

for v in value:
for i, opt in enumerate(options):
if opt == v:
selected.append(i)
# case insensitive check - only do if matching case value is not present
elif v not in options and opt.lower() == v.lower():
selected.append(i)

self.widget.setSelectedItems(selected)
else:
self.widget.setBand(value)
else:
self.setComboValue(value)

def value(self):
if self.dialogType in (DIALOG_STANDARD, DIALOG_BATCH):
f = self.widget.currentBand()
if self.param.flags() & QgsProcessingParameterDefinition.FlagOptional and not f:
return None
if self.param.allowMultiple():
return [self.widget.options[i] for i in self.widget.selectedoptions]
else:
f = self.widget.currentBand()
if self.param.flags() & QgsProcessingParameterDefinition.FlagOptional and not f:
return None
return f
else:
def validator(v):
Expand Down
68 changes: 61 additions & 7 deletions src/core/processing/qgsprocessingparameters.cpp
Expand Up @@ -4276,9 +4276,10 @@ QgsProcessingParameterVectorDestination *QgsProcessingParameterVectorDestination
return new QgsProcessingParameterVectorDestination( name, description, type, definition, isOptional );
}

QgsProcessingParameterBand::QgsProcessingParameterBand( const QString &name, const QString &description, const QVariant &defaultValue, const QString &parentLayerParameterName, bool optional )
QgsProcessingParameterBand::QgsProcessingParameterBand( const QString &name, const QString &description, const QVariant &defaultValue, const QString &parentLayerParameterName, bool optional, bool allowMultiple )
: QgsProcessingParameterDefinition( name, description, defaultValue, optional )
, mParentLayerParameterName( parentLayerParameterName )
, mAllowMultiple( allowMultiple )
{

}
Expand All @@ -4298,15 +4299,35 @@ bool QgsProcessingParameterBand::checkValueIsAcceptable( const QVariant &input,
return true;
}

bool ok = false;
double res = input.toInt( &ok );
Q_UNUSED( res );
if ( !ok )
return mFlags & FlagOptional;
if ( input.type() == QVariant::List || input.type() == QVariant::StringList )
{
if ( !mAllowMultiple )
return false;

if ( input.toList().isEmpty() && !( mFlags & FlagOptional ) )
return false;
}
else
{
bool ok = false;
double res = input.toInt( &ok );
Q_UNUSED( res );
if ( !ok )
return mFlags & FlagOptional;
}
return true;
}

bool QgsProcessingParameterBand::allowMultiple() const
{
return mAllowMultiple;
}

void QgsProcessingParameterBand::setAllowMultiple( bool allowMultiple )
{
mAllowMultiple = allowMultiple;
}

QString QgsProcessingParameterBand::valueAsPythonString( const QVariant &value, QgsProcessingContext & ) const
{
if ( !value.isValid() )
Expand All @@ -4315,6 +4336,27 @@ QString QgsProcessingParameterBand::valueAsPythonString( const QVariant &value,
if ( value.canConvert<QgsProperty>() )
return QStringLiteral( "QgsProperty.fromExpression('%1')" ).arg( value.value< QgsProperty >().asExpression() );

if ( value.type() == QVariant::List )
{
QStringList parts;
QVariantList values = value.toList();
for ( auto it = values.constBegin(); it != values.constEnd(); ++it )
{
parts << QgsProcessingUtils::stringToPythonLiteral( it->toString() );
}
return parts.join( ',' ).prepend( '[' ).append( ']' );
}
else if ( value.type() == QVariant::StringList )
{
QStringList parts;
QStringList values = value.toStringList();
for ( auto it = values.constBegin(); it != values.constEnd(); ++it )
{
parts << QgsProcessingUtils::stringToPythonLiteral( *it );
}
return parts.join( ',' ).prepend( '[' ).append( ']' );
}

return value.toString();
}

Expand All @@ -4325,6 +4367,9 @@ QString QgsProcessingParameterBand::asScriptCode() const
code += QStringLiteral( "optional " );
code += QStringLiteral( "band " );

if ( mAllowMultiple )
code += QStringLiteral( "multiple " );

code += mParentLayerParameterName + ' ';

code += mDefault.toString();
Expand Down Expand Up @@ -4353,20 +4398,29 @@ QVariantMap QgsProcessingParameterBand::toVariantMap() const
{
QVariantMap map = QgsProcessingParameterDefinition::toVariantMap();
map.insert( QStringLiteral( "parent_layer" ), mParentLayerParameterName );
map.insert( QStringLiteral( "allow_multiple" ), mAllowMultiple );
return map;
}

bool QgsProcessingParameterBand::fromVariantMap( const QVariantMap &map )
{
QgsProcessingParameterDefinition::fromVariantMap( map );
mParentLayerParameterName = map.value( QStringLiteral( "parent_layer" ) ).toString();
mAllowMultiple = map.value( QStringLiteral( "allow_multiple" ) ).toBool();
return true;
}

QgsProcessingParameterBand *QgsProcessingParameterBand::fromScriptCode( const QString &name, const QString &description, bool isOptional, const QString &definition )
{
QString parent;
QString def = definition;
bool allowMultiple = false;

if ( def.startsWith( QStringLiteral( "multiple" ), Qt::CaseInsensitive ) )
{
allowMultiple = true;
def = def.mid( 8 ).trimmed();
}

QRegularExpression re( QStringLiteral( "(.*?)\\s+(.*)$" ) );
QRegularExpressionMatch m = re.match( def );
Expand All @@ -4381,7 +4435,7 @@ QgsProcessingParameterBand *QgsProcessingParameterBand::fromScriptCode( const QS
def.clear();
}

return new QgsProcessingParameterBand( name, description, def.isEmpty() ? QVariant() : def, parent, isOptional );
return new QgsProcessingParameterBand( name, description, def.isEmpty() ? QVariant() : def, parent, isOptional, allowMultiple );
}

//
Expand Down
18 changes: 17 additions & 1 deletion src/core/processing/qgsprocessingparameters.h
Expand Up @@ -2346,7 +2346,8 @@ class CORE_EXPORT QgsProcessingParameterBand : public QgsProcessingParameterDefi
*/
QgsProcessingParameterBand( const QString &name, const QString &description = QString(), const QVariant &defaultValue = QVariant(),
const QString &parentLayerParameterName = QString(),
bool optional = false );
bool optional = false,
bool allowMultiple = false );

/**
* Returns the type name for the parameter class.
Expand Down Expand Up @@ -2379,9 +2380,24 @@ class CORE_EXPORT QgsProcessingParameterBand : public QgsProcessingParameterDefi
*/
static QgsProcessingParameterBand *fromScriptCode( const QString &name, const QString &description, bool isOptional, const QString &definition ) SIP_FACTORY;

/**
* Returns whether multiple band selections are permitted.
* \see setAllowMultiple()
* \since QGIS 3.4
*/
bool allowMultiple() const;

/**
* Sets whether multiple band selections are permitted.
* \see allowMultiple()
* \since QGIS 3.4
*/
void setAllowMultiple( bool allowMultiple );

private:

QString mParentLayerParameterName;
bool mAllowMultiple = false;
};

// clazy:excludeall=qstring-allocations
Expand Down
40 changes: 40 additions & 0 deletions tests/src/analysis/testqgsprocessing.cpp
Expand Up @@ -5103,6 +5103,46 @@ void TestQgsProcessing::parameterBand()
QCOMPARE( fromCode->defaultValue(), def->defaultValue() );
QCOMPARE( fromCode->parentLayerParameterName(), def->parentLayerParameterName() );

// multiple
def.reset( new QgsProcessingParameterBand( "non_optional", QString(), QVariant(), QString(), false, true ) );
QVERIFY( def->checkValueIsAcceptable( QStringList() << "Band 1" << "Band 2" ) );
QVERIFY( def->checkValueIsAcceptable( QVariantList() << "Band 1" << "Band 2" ) );
QVERIFY( !def->checkValueIsAcceptable( "" ) );
QVERIFY( !def->checkValueIsAcceptable( QVariant() ) );
QVERIFY( !def->checkValueIsAcceptable( QStringList() ) );
QVERIFY( !def->checkValueIsAcceptable( QVariantList() ) );

params.insert( "non_optional", QString( "Band 1;Band 2" ) );
QStringList bands = QgsProcessingParameters::parameterAsFields( def.get(), params, context );
QCOMPARE( bands, QStringList() << "Band 1" << "Band 2" );
params.insert( "non_optional", QVariantList() << "Band 1" << "Band 2" );
bands = QgsProcessingParameters::parameterAsFields( def.get(), params, context );
QCOMPARE( bands, QStringList() << "Band 1" << "Band 2" );

QCOMPARE( def->valueAsPythonString( QVariant(), context ), QStringLiteral( "None" ) );
QCOMPARE( def->valueAsPythonString( QStringList() << "Band 1" << "Band 2", context ), QStringLiteral( "['Band 1','Band 2']" ) );
QCOMPARE( def->valueAsPythonString( QVariantList() << "Band 1" << "Band 2", context ), QStringLiteral( "['Band 1','Band 2']" ) );

QVariantMap map = def->toVariantMap();
QgsProcessingParameterBand fromMap( "x" );
QVERIFY( fromMap.fromVariantMap( map ) );
QCOMPARE( fromMap.name(), def->name() );
QCOMPARE( fromMap.description(), def->description() );
QCOMPARE( fromMap.defaultValue(), def->defaultValue() );
QCOMPARE( fromMap.parentLayerParameterName(), def->parentLayerParameterName() );
QCOMPARE( fromMap.allowMultiple(), def->allowMultiple() );
def.reset( dynamic_cast< QgsProcessingParameterBand *>( QgsProcessingParameters::parameterFromVariantMap( map ) ) );
QVERIFY( dynamic_cast< QgsProcessingParameterBand *>( def.get() ) );

code = def->asScriptCode();
fromCode.reset( dynamic_cast< QgsProcessingParameterBand * >( QgsProcessingParameters::parameterFromScriptCode( code ) ) );
QVERIFY( fromCode.get() );
QCOMPARE( fromCode->name(), def->name() );
QCOMPARE( fromCode->description(), QStringLiteral( "non optional" ) );
QCOMPARE( fromCode->defaultValue(), def->defaultValue() );
QCOMPARE( fromCode->parentLayerParameterName(), def->parentLayerParameterName() );
QCOMPARE( fromCode->allowMultiple(), def->allowMultiple() );

// optional
def.reset( new QgsProcessingParameterBand( "optional", QString(), 1, QString(), true ) );
QVERIFY( def->checkValueIsAcceptable( 1 ) );
Expand Down

0 comments on commit 13ecaa4

Please sign in to comment.