Skip to content

Commit

Permalink
[processing] add "not set" support for range parameter (fix #29374, r…
Browse files Browse the repository at this point in the history
…efs #29269)
  • Loading branch information
alexbruy committed Jan 2, 2020
1 parent d8645f5 commit b5d71d3
Show file tree
Hide file tree
Showing 5 changed files with 159 additions and 17 deletions.
22 changes: 19 additions & 3 deletions src/core/processing/qgsprocessingparameters.cpp
Expand Up @@ -1447,6 +1447,7 @@ QList<double> QgsProcessingParameters::parameterAsRange( const QgsProcessingPara

QStringList resultStringList;
QVariant val = value;

if ( val.canConvert<QgsProperty>() )
resultStringList << val.value< QgsProperty >().valueAsString( context.expressionContext(), definition->defaultValue().toString() );
else if ( val.type() == QVariant::List )
Expand Down Expand Up @@ -1478,9 +1479,23 @@ QList<double> QgsProcessingParameters::parameterAsRange( const QgsProcessingPara
}

if ( resultStringList.size() < 2 )
return QList< double >() << 0.0 << 0.0;
return QList< double >() << NAN << NAN;

return QList< double >() << resultStringList.at( 0 ).toDouble() << resultStringList.at( 1 ).toDouble();
QList< double > result;
bool ok = false;
double n = resultStringList.at( 0 ).toDouble( &ok );
if ( ok )
result << n;
else
result << NAN;
ok = false;
n = resultStringList.at( 1 ).toDouble( &ok );
if ( ok )
result << n;
else
result << NAN;

return result;
}

QStringList QgsProcessingParameters::parameterAsFields( const QgsProcessingParameterDefinition *definition, const QVariantMap &parameters, QgsProcessingContext &context )
Expand Down Expand Up @@ -3127,7 +3142,8 @@ bool QgsProcessingParameterRange::fromVariantMap( const QVariantMap &map )

QgsProcessingParameterRange *QgsProcessingParameterRange::fromScriptCode( const QString &name, const QString &description, bool isOptional, const QString &definition )
{
return new QgsProcessingParameterRange( name, description, QgsProcessingParameterNumber::Double, definition.isEmpty() ? QVariant() : definition, isOptional );
return new QgsProcessingParameterRange( name, description, QgsProcessingParameterNumber::Double, definition.isEmpty() ? QVariant()
: ( definition.toLower().trimmed() == QStringLiteral( "none" ) ? QVariant() : definition ), isOptional );
}

QgsProcessingParameterRasterLayer::QgsProcessingParameterRasterLayer( const QString &name, const QString &description, const QVariant &defaultValue, bool optional )
Expand Down
80 changes: 70 additions & 10 deletions src/gui/processing/qgsprocessingwidgetwrapperimpl.cpp
Expand Up @@ -1113,12 +1113,28 @@ QWidget *QgsProcessingRangeWidgetWrapper::createWidget()
mMinSpinBox->setMaximum( 99999999.999999 );
mMaxSpinBox->setMaximum( 99999999.999999 );

if ( rangeDef->flags() & QgsProcessingParameterDefinition::FlagOptional )
{
mAllowingNull = true;

const double min = mMinSpinBox->minimum() - 1;
mMinSpinBox->setMinimum( min );
mMaxSpinBox->setMinimum( min );
mMinSpinBox->setValue( min );
mMaxSpinBox->setValue( min );

mMinSpinBox->setShowClearButton( true );
mMaxSpinBox->setShowClearButton( true );
mMinSpinBox->setSpecialValueText( tr( "Not set" ) );
mMaxSpinBox->setSpecialValueText( tr( "Not set" ) );
}

w->setToolTip( parameterDefinition()->toolTip() );

connect( mMinSpinBox, qgis::overload<double>::of( &QgsDoubleSpinBox::valueChanged ), this, [ = ]( const double v )
{
mBlockChangedSignal++;
if ( v > mMaxSpinBox->value() )
if ( !mAllowingNull && v > mMaxSpinBox->value() )
mMaxSpinBox->setValue( v );
mBlockChangedSignal--;

Expand All @@ -1128,7 +1144,7 @@ QWidget *QgsProcessingRangeWidgetWrapper::createWidget()
connect( mMaxSpinBox, qgis::overload<double>::of( &QgsDoubleSpinBox::valueChanged ), this, [ = ]( const double v )
{
mBlockChangedSignal++;
if ( v < mMinSpinBox->value() )
if ( !mAllowingNull && v < mMinSpinBox->value() )
mMinSpinBox->setValue( v );
mBlockChangedSignal--;

Expand All @@ -1145,22 +1161,66 @@ QWidget *QgsProcessingRangeWidgetWrapper::createWidget()
void QgsProcessingRangeWidgetWrapper::setWidgetValue( const QVariant &value, QgsProcessingContext &context )
{
const QList< double > v = QgsProcessingParameters::parameterAsRange( parameterDefinition(), value, context );
if ( v.empty() )
return;
if ( mAllowingNull && v.empty() )
{
mMinSpinBox->clear();
mMaxSpinBox->clear();
}
else
{
if ( v.empty() )
return;

mBlockChangedSignal++;
mMinSpinBox->setValue( v.at( 0 ) );
if ( v.count() >= 2 )
mMaxSpinBox->setValue( v.at( 1 ) );
mBlockChangedSignal--;
if ( mAllowingNull )
{
mBlockChangedSignal++;
if ( std::isnan( v.at( 0 ) ) )
mMinSpinBox->clear();
else
mMinSpinBox->setValue( v.at( 0 ) );

if ( v.count() >= 2 )
{
if ( std::isnan( v.at( 1 ) ) )
mMaxSpinBox->clear();
else
mMaxSpinBox->setValue( v.at( 1 ) );
}
mBlockChangedSignal--;
}
else
{
mBlockChangedSignal++;
mMinSpinBox->setValue( v.at( 0 ) );
if ( v.count() >= 2 )
mMaxSpinBox->setValue( v.at( 1 ) );
mBlockChangedSignal--;
}
}

if ( !mBlockChangedSignal )
emit widgetValueHasChanged( this );
}

QVariant QgsProcessingRangeWidgetWrapper::widgetValue() const
{
return QStringLiteral( "%1,%2" ).arg( mMinSpinBox->value() ).arg( mMaxSpinBox->value() );
if ( mAllowingNull )
{
QString value;
if ( qgsDoubleNear( mMinSpinBox->value(), mMinSpinBox->minimum() ) )
value = QStringLiteral( "None" );
else
value = QStringLiteral( "%1" ).arg( mMinSpinBox->value() );

if ( qgsDoubleNear( mMaxSpinBox->value(), mMaxSpinBox->minimum() ) )
value += QStringLiteral( ",None" );
else
value += QStringLiteral( ",%1" ).arg( mMaxSpinBox->value() );

return value;
}
else
return QStringLiteral( "%1,%2" ).arg( mMinSpinBox->value() ).arg( mMaxSpinBox->value() );
}

QStringList QgsProcessingRangeWidgetWrapper::compatibleParameterTypes() const
Expand Down
1 change: 1 addition & 0 deletions src/gui/processing/qgsprocessingwidgetwrapperimpl.h
Expand Up @@ -380,6 +380,7 @@ class GUI_EXPORT QgsProcessingRangeWidgetWrapper : public QgsAbstractProcessingP
private:

int mBlockChangedSignal = 0;
bool mAllowingNull = false;

friend class TestProcessingGui;
};
Expand Down
51 changes: 47 additions & 4 deletions tests/src/analysis/testqgsprocessing.cpp
Expand Up @@ -392,8 +392,6 @@ class DummyProviderNoLoad : public DummyProvider // clazy:exclude=missing-qobjec

};



class DummyAlgorithm2 : public QgsProcessingAlgorithm
{
public:
Expand All @@ -419,7 +417,6 @@ class DummyAlgorithm2 : public QgsProcessingAlgorithm

};


class DummyProvider3 : public QgsProcessingProvider // clazy:exclude=missing-qobject-macro
{
public:
Expand Down Expand Up @@ -3930,7 +3927,6 @@ void TestQgsProcessing::parameterRange()
QCOMPARE( fromCode->flags(), def->flags() );
QCOMPARE( fromCode->defaultValue(), def->defaultValue() );


QVariantMap map = def->toVariantMap();
QgsProcessingParameterRange fromMap( "x" );
QVERIFY( fromMap.fromVariantMap( map ) );
Expand Down Expand Up @@ -3965,6 +3961,53 @@ void TestQgsProcessing::parameterRange()
QCOMPARE( fromCode->description(), QStringLiteral( "optional" ) );
QCOMPARE( fromCode->flags(), def->flags() );
QCOMPARE( fromCode->defaultValue(), def->defaultValue() );

// optional, no default value
def.reset( new QgsProcessingParameterRange( "optional", QString(), QgsProcessingParameterNumber::Double, QVariant(), true ) );
QVERIFY( def->checkValueIsAcceptable( "1.1,2" ) );
QVERIFY( def->checkValueIsAcceptable( QVariantList() << 1.1 << 2 ) );
QVERIFY( def->checkValueIsAcceptable( "" ) );
QVERIFY( def->checkValueIsAcceptable( QVariant() ) );

params.insert( "optional", QVariant() );
range = QgsProcessingParameters::parameterAsRange( def.get(), params, context );
QVERIFY( std::isnan( range.at( 0 ) ) );
QVERIFY( std::isnan( range.at( 1 ) ) );

params.insert( "optional", QStringLiteral( "None,2" ) );
range = QgsProcessingParameters::parameterAsRange( def.get(), params, context );
QVERIFY( std::isnan( range.at( 0 ) ) );
QGSCOMPARENEAR( range.at( 1 ), 2, 0.001 );

params.insert( "optional", QStringLiteral( "1.2,None" ) );
range = QgsProcessingParameters::parameterAsRange( def.get(), params, context );
QGSCOMPARENEAR( range.at( 0 ), 1.2, 0.001 );
QVERIFY( std::isnan( range.at( 1 ) ) );

params.insert( "optional", QStringLiteral( "None,None" ) );
range = QgsProcessingParameters::parameterAsRange( def.get(), params, context );
QVERIFY( std::isnan( range.at( 0 ) ) );
QVERIFY( std::isnan( range.at( 1 ) ) );

params.insert( "optional", QStringLiteral( "None" ) );
range = QgsProcessingParameters::parameterAsRange( def.get(), params, context );
QVERIFY( std::isnan( range.at( 0 ) ) );
QVERIFY( std::isnan( range.at( 1 ) ) );

params.insert( "optional", QVariant() );
range = QgsProcessingParameters::parameterAsRange( def.get(), params, context );
QVERIFY( std::isnan( range.at( 0 ) ) );
QVERIFY( std::isnan( range.at( 1 ) ) );

pythonCode = def->asPythonString();
QCOMPARE( pythonCode, QStringLiteral( "QgsProcessingParameterRange('optional', '', optional=True, type=QgsProcessingParameterNumber.Double, defaultValue=None)" ) );

fromCode.reset( dynamic_cast< QgsProcessingParameterRange * >( QgsProcessingParameters::parameterFromScriptCode( QStringLiteral( "##optional=optional range None" ) ) ) );
QVERIFY( fromCode.get() );
QCOMPARE( fromCode->name(), def->name() );
QCOMPARE( fromCode->description(), QStringLiteral( "optional" ) );
QCOMPARE( fromCode->flags(), def->flags() );
QVERIFY( !fromCode->defaultValue().isValid() );
}

void TestQgsProcessing::parameterRasterLayer()
Expand Down
22 changes: 22 additions & 0 deletions tests/src/gui/testprocessinggui.cpp
Expand Up @@ -1976,6 +1976,28 @@ void TestProcessingGui::testRangeWrapper()
QCOMPARE( wrapper2.parameterValue().toString(), QStringLiteral( "100,100" ) );

delete w;

// optional
QgsProcessingParameterRange paramOptional( QStringLiteral( "range" ), QStringLiteral( "range" ), QgsProcessingParameterNumber::Double, QVariant(), true );

QgsProcessingRangeWidgetWrapper wrapperOptional( &paramOptional, type );

w = wrapperOptional.createWrappedWidget( context );
QCOMPARE( wrapperOptional.parameterValue().toString(), QStringLiteral( "None,None" ) );
wrapperOptional.setParameterValue( QStringLiteral( "1,100" ), context );
QCOMPARE( wrapperOptional.parameterValue().toString(), QStringLiteral( "1,100" ) );
wrapperOptional.setParameterValue( QStringLiteral( "None,100" ), context );
QCOMPARE( wrapperOptional.parameterValue().toString(), QStringLiteral( "None,100" ) );
wrapperOptional.setParameterValue( QStringLiteral( "1,None" ), context );
QCOMPARE( wrapperOptional.parameterValue().toString(), QStringLiteral( "1,None" ) );
wrapperOptional.setParameterValue( QStringLiteral( "None,None" ), context );
QCOMPARE( wrapperOptional.parameterValue().toString(), QStringLiteral( "None,None" ) );
wrapperOptional.setParameterValue( QStringLiteral( "None" ), context );
QCOMPARE( wrapperOptional.parameterValue().toString(), QStringLiteral( "None,None" ) );
wrapperOptional.setParameterValue( QVariant(), context );
QCOMPARE( wrapperOptional.parameterValue().toString(), QStringLiteral( "None,None" ) );

delete w;
};

// standard wrapper
Expand Down

0 comments on commit b5d71d3

Please sign in to comment.