Skip to content

Commit

Permalink
Allow creation of color parameters with no opacity control
Browse files Browse the repository at this point in the history
  • Loading branch information
nyalldawson committed Jun 30, 2019
1 parent 8fc63bf commit d96c738
Show file tree
Hide file tree
Showing 7 changed files with 170 additions and 18 deletions.
Expand Up @@ -3055,6 +3055,8 @@ class QgsProcessingParameterColor : QgsProcessingParameterDefinition
%Docstring
A color parameter for processing algorithms.

QgsProcessingParameterColor should be evaluated by calling :py:func:`QgsProcessingAlgorithm.parameterAsColor()`

.. versionadded:: 3.10
%End

Expand All @@ -3064,9 +3066,12 @@ A color parameter for processing algorithms.
public:

QgsProcessingParameterColor( const QString &name, const QString &description = QString(), const QVariant &defaultValue = QVariant(),
bool opacityEnabled = true,
bool optional = false );
%Docstring
Constructor for QgsProcessingParameterColor.

If ``opacityEnabled`` is ``True``, then users will have the option of varying color opacity.
%End

static QString typeName();
Expand All @@ -3084,6 +3089,28 @@ Returns the type name for the parameter class.

virtual bool checkValueIsAcceptable( const QVariant &input, QgsProcessingContext *context = 0 ) const;

virtual QVariantMap toVariantMap() const;

virtual bool fromVariantMap( const QVariantMap &map );


bool opacityEnabled() const;
%Docstring
Returns ``True`` if the parameter allows opacity control.

The default behavior is to allow users to set opacity for the color.

.. seealso:: :py:func:`setOpacityEnabled`
%End

void setOpacityEnabled( bool enabled );
%Docstring
Sets whether the parameter allows opacity control.

The default behavior is to allow users to set opacity for the color.

.. seealso:: :py:func:`opacityEnabled`
%End

static QgsProcessingParameterColor *fromScriptCode( const QString &name, const QString &description, bool isOptional, const QString &definition ) /Factory/;
%Docstring
Expand Down
62 changes: 57 additions & 5 deletions src/core/processing/qgsprocessingparameters.cpp
Expand Up @@ -1583,7 +1583,11 @@ QColor QgsProcessingParameters::parameterAsColor( const QgsProcessingParameterDe
}
if ( val.type() == QVariant::Color )
{
return val.value<QColor>();
QColor c = val.value< QColor >();
if ( const QgsProcessingParameterColor *colorParam = dynamic_cast< const QgsProcessingParameterColor * >( definition ) )
if ( !colorParam->opacityEnabled() )
c.setAlpha( 255 );
return c;
}

QString colorText = parameterAsString( definition, value, context );
Expand All @@ -1599,7 +1603,11 @@ QColor QgsProcessingParameters::parameterAsColor( const QgsProcessingParameterDe
return QColor();

bool containsAlpha = false;
return QgsSymbolLayerUtils::parseColorWithAlpha( colorText, containsAlpha );
QColor c = QgsSymbolLayerUtils::parseColorWithAlpha( colorText, containsAlpha );
if ( const QgsProcessingParameterColor *colorParam = dynamic_cast< const QgsProcessingParameterColor * >( definition ) )
if ( c.isValid() && !colorParam->opacityEnabled() )
c.setAlpha( 255 );
return c;
}

QgsProcessingParameterDefinition *QgsProcessingParameters::parameterFromVariantMap( const QVariantMap &map )
Expand Down Expand Up @@ -5634,8 +5642,9 @@ void QgsProcessingParameterLayoutItem::setItemType( int type )
// QgsProcessingParameterColor
//

QgsProcessingParameterColor::QgsProcessingParameterColor( const QString &name, const QString &description, const QVariant &defaultValue, bool optional )
QgsProcessingParameterColor::QgsProcessingParameterColor( const QString &name, const QString &description, const QVariant &defaultValue, bool opacityEnabled, bool optional )
: QgsProcessingParameterDefinition( name, description, defaultValue, optional )
, mAllowOpacity( opacityEnabled )
{

}
Expand All @@ -5657,7 +5666,13 @@ QString QgsProcessingParameterColor::valueAsPythonString( const QVariant &value,
return QStringLiteral( "QColor()" );

if ( value.canConvert< QColor >() )
return QStringLiteral( "'%1'" ).arg( value.value< QColor >().name() );
{
QColor c = value.value< QColor >();
if ( !mAllowOpacity || c.alpha() == 255 )
return QStringLiteral( "QColor(%1, %2, %3)" ).arg( c.red() ).arg( c.green() ).arg( c.blue() );
else
return QStringLiteral( "QColor(%1, %2, %3, %4)" ).arg( c.red() ).arg( c.green() ).arg( c.blue() ).arg( c.alpha() );
}

QString s = value.toString();
return QgsProcessingUtils::stringToPythonLiteral( s );
Expand All @@ -5670,6 +5685,9 @@ QString QgsProcessingParameterColor::asScriptCode() const
code += QStringLiteral( "optional " );
code += QStringLiteral( "color " );

if ( mAllowOpacity )
code += QStringLiteral( "withopacity " );

code += mDefault.toString();
return code.trimmed();
}
Expand All @@ -5684,6 +5702,8 @@ QString QgsProcessingParameterColor::asPythonString( const QgsProcessing::Python
if ( mFlags & FlagOptional )
code += QStringLiteral( ", optional=True" );

code += QStringLiteral( ", opacityEnabled=%1" ).arg( mAllowOpacity ? QStringLiteral( "True" ) : QStringLiteral( "False" ) );

QgsProcessingContext c;
code += QStringLiteral( ", defaultValue=%1)" ).arg( valueAsPythonString( mDefault, c ) );
return code;
Expand Down Expand Up @@ -5716,9 +5736,41 @@ bool QgsProcessingParameterColor::checkValueIsAcceptable( const QVariant &input,
return QgsSymbolLayerUtils::parseColorWithAlpha( input.toString(), containsAlpha ).isValid();
}

QVariantMap QgsProcessingParameterColor::toVariantMap() const
{
QVariantMap map = QgsProcessingParameterDefinition::toVariantMap();
map.insert( QStringLiteral( "opacityEnabled" ), mAllowOpacity );
return map;
}

bool QgsProcessingParameterColor::fromVariantMap( const QVariantMap &map )
{
QgsProcessingParameterDefinition::fromVariantMap( map );
mAllowOpacity = map.value( QStringLiteral( "opacityEnabled" ) ).toBool();
return true;
}

bool QgsProcessingParameterColor::opacityEnabled() const
{
return mAllowOpacity;
}

void QgsProcessingParameterColor::setOpacityEnabled( bool enabled )
{
mAllowOpacity = enabled;
}

QgsProcessingParameterColor *QgsProcessingParameterColor::fromScriptCode( const QString &name, const QString &description, bool isOptional, const QString &definition )
{
QString def = definition;

bool allowOpacity = false;
if ( def.startsWith( QLatin1String( "withopacity" ), Qt::CaseInsensitive ) )
{
allowOpacity = true;
def = def.mid( 12 );
}

if ( def.startsWith( '"' ) || def.startsWith( '\'' ) )
def = def.mid( 1 );
if ( def.endsWith( '"' ) || def.endsWith( '\'' ) )
Expand All @@ -5728,5 +5780,5 @@ QgsProcessingParameterColor *QgsProcessingParameterColor::fromScriptCode( const
if ( def == QStringLiteral( "None" ) )
defaultValue = QVariant();

return new QgsProcessingParameterColor( name, description, defaultValue, isOptional );
return new QgsProcessingParameterColor( name, description, defaultValue, allowOpacity, isOptional );
}
31 changes: 30 additions & 1 deletion src/core/processing/qgsprocessingparameters.h
Expand Up @@ -2860,16 +2860,22 @@ class CORE_EXPORT QgsProcessingParameterLayoutItem : public QgsProcessingParamet
* \class QgsProcessingParameterColor
* \ingroup core
* A color parameter for processing algorithms.
* \since QGIS 3.10
*
* QgsProcessingParameterColor should be evaluated by calling QgsProcessingAlgorithm::parameterAsColor().
*
* \since QGIS 3.10
*/
class CORE_EXPORT QgsProcessingParameterColor : public QgsProcessingParameterDefinition
{
public:

/**
* Constructor for QgsProcessingParameterColor.
*
* If \a opacityEnabled is TRUE, then users will have the option of varying color opacity.
*/
QgsProcessingParameterColor( const QString &name, const QString &description = QString(), const QVariant &defaultValue = QVariant(),
bool opacityEnabled = true,
bool optional = false );

/**
Expand All @@ -2882,12 +2888,35 @@ class CORE_EXPORT QgsProcessingParameterColor : public QgsProcessingParameterDef
QString asScriptCode() const override;
QString asPythonString( QgsProcessing::PythonOutputType outputType = QgsProcessing::PythonQgsProcessingAlgorithmSubclass ) const override;
bool checkValueIsAcceptable( const QVariant &input, QgsProcessingContext *context = nullptr ) const override;
QVariantMap toVariantMap() const override;
bool fromVariantMap( const QVariantMap &map ) override;

/**
* Returns TRUE if the parameter allows opacity control.
*
* The default behavior is to allow users to set opacity for the color.
* \see setOpacityEnabled()
*/
bool opacityEnabled() const;

/**
* Sets whether the parameter allows opacity control.
*
* The default behavior is to allow users to set opacity for the color.
*
* \see opacityEnabled()
*/
void setOpacityEnabled( bool enabled );

/**
* Creates a new parameter using the definition from a script code.
*/
static QgsProcessingParameterColor *fromScriptCode( const QString &name, const QString &description, bool isOptional, const QString &definition ) SIP_FACTORY;

private:

bool mAllowOpacity = true;

};

// clazy:excludeall=qstring-allocations
Expand Down
6 changes: 6 additions & 0 deletions src/gui/processing/qgsprocessingwidgetwrapperimpl.cpp
Expand Up @@ -2418,7 +2418,13 @@ QWidget *QgsProcessingColorWidgetWrapper::createWidget()
if ( colorParam->flags() & QgsProcessingParameterDefinition::FlagOptional )
mColorButton->setShowNull( true );

mColorButton->setAllowOpacity( colorParam->opacityEnabled() );
mColorButton->setToolTip( parameterDefinition()->toolTip() );
mColorButton->setColorDialogTitle( parameterDefinition()->description() );
if ( colorParam->defaultValue().value< QColor >().isValid() )
{
mColorButton->setDefaultColor( colorParam->defaultValue().value< QColor >() );
}

connect( mColorButton, &QgsColorButton::colorChanged, this, [ = ]
{
Expand Down
48 changes: 37 additions & 11 deletions tests/src/analysis/testqgsprocessing.cpp
Expand Up @@ -6268,7 +6268,12 @@ void TestQgsProcessing::parameterColor()
QgsProcessingContext context;

// not optional!
std::unique_ptr< QgsProcessingParameterColor > def( new QgsProcessingParameterColor( "non_optional", QString(), QString(), false ) );
std::unique_ptr< QgsProcessingParameterColor > def( new QgsProcessingParameterColor( "non_optional", QString(), QString(), true, false ) );
QVERIFY( def->opacityEnabled() );
def->setOpacityEnabled( false );
QVERIFY( !def->opacityEnabled() );
def->setOpacityEnabled( true );

QVERIFY( !def->checkValueIsAcceptable( 1 ) );
QVERIFY( def->checkValueIsAcceptable( "#ff0000" ) );
QVERIFY( !def->checkValueIsAcceptable( "bbbbbbbbbbbbbbbbbbbb" ) );
Expand All @@ -6286,23 +6291,27 @@ void TestQgsProcessing::parameterColor()
QCOMPARE( QgsProcessingParameters::parameterAsColor( def.get(), params, context ).name(), QString( "#ff0000" ) );
params.insert( "non_optional", QColor( 255, 255, 0 ) );
QCOMPARE( QgsProcessingParameters::parameterAsColor( def.get(), params, context ).name(), QString( "#ffff00" ) );
params.insert( "non_optional", QColor( 255, 255, 0, 100 ) );
QCOMPARE( QgsProcessingParameters::parameterAsColor( def.get(), params, context ), QColor( 255, 255, 0, 100 ) );

QCOMPARE( def->valueAsPythonString( QVariant(), context ), QStringLiteral( "None" ) );
QCOMPARE( def->valueAsPythonString( QStringLiteral( "#ff0000" ), context ), QStringLiteral( "'#ff0000'" ) );
QCOMPARE( def->valueAsPythonString( QStringLiteral( "#ff0000" ), context ), QStringLiteral( "QColor(255, 0, 0)" ) );
QCOMPARE( def->valueAsPythonString( QColor(), context ), QStringLiteral( "QColor()" ) );
QCOMPARE( def->valueAsPythonString( QColor( 255, 0, 0 ), context ), QStringLiteral( "'#ff0000'" ) );
QCOMPARE( def->valueAsPythonString( QColor( 255, 0, 0 ), context ), QStringLiteral( "QColor(255, 0, 0)" ) );
QCOMPARE( def->valueAsPythonString( QColor( 255, 0, 0, 100 ), context ), QStringLiteral( "QColor(255, 0, 0, 100)" ) );

QString pythonCode = def->asPythonString();
QCOMPARE( pythonCode, QStringLiteral( "QgsProcessingParameterColor('non_optional', '', defaultValue=None)" ) );
QCOMPARE( pythonCode, QStringLiteral( "QgsProcessingParameterColor('non_optional', '', opacityEnabled=True, defaultValue=None)" ) );

QString code = def->asScriptCode();
QCOMPARE( code, QStringLiteral( "##non_optional=color" ) );
QCOMPARE( code, QStringLiteral( "##non_optional=color withopacity" ) );
std::unique_ptr< QgsProcessingParameterColor > fromCode( dynamic_cast< QgsProcessingParameterColor * >( QgsProcessingParameters::parameterFromScriptCode( code ) ) );
QVERIFY( fromCode.get() );
QCOMPARE( fromCode->name(), def->name() );
QCOMPARE( fromCode->description(), QStringLiteral( "non optional" ) );
QCOMPARE( fromCode->flags(), def->flags() );
QCOMPARE( fromCode->defaultValue(), def->defaultValue() );
QVERIFY( fromCode->opacityEnabled() );

QVariantMap map = def->toVariantMap();
QgsProcessingParameterColor fromMap( "x" );
Expand All @@ -6311,26 +6320,34 @@ void TestQgsProcessing::parameterColor()
QCOMPARE( fromMap.description(), def->description() );
QCOMPARE( fromMap.flags(), def->flags() );
QCOMPARE( fromMap.defaultValue(), def->defaultValue() );
QVERIFY( fromMap.opacityEnabled() );
def.reset( dynamic_cast< QgsProcessingParameterColor *>( QgsProcessingParameters::parameterFromVariantMap( map ) ) );
QVERIFY( dynamic_cast< QgsProcessingParameterColor *>( def.get() ) );

fromCode.reset( dynamic_cast< QgsProcessingParameterColor * >( QgsProcessingParameters::parameterFromScriptCode( QStringLiteral( "##non_optional=color None" ) ) ) );
fromCode.reset( dynamic_cast< QgsProcessingParameterColor * >( QgsProcessingParameters::parameterFromScriptCode( QStringLiteral( "##non_optional=color withopacity None" ) ) ) );
QVERIFY( fromCode.get() );
QCOMPARE( fromCode->name(), def->name() );
QCOMPARE( fromCode->description(), QStringLiteral( "non optional" ) );
QCOMPARE( fromCode->flags(), def->flags() );
QVERIFY( !fromCode->defaultValue().isValid() );
QVERIFY( fromCode->opacityEnabled() );

fromCode.reset( dynamic_cast< QgsProcessingParameterColor * >( QgsProcessingParameters::parameterFromScriptCode( QStringLiteral( "##non_optional=color #aabbcc" ) ) ) );
QVERIFY( fromCode.get() );
QCOMPARE( fromCode->name(), def->name() );
QCOMPARE( fromCode->description(), QStringLiteral( "non optional" ) );
QCOMPARE( fromCode->flags(), def->flags() );
QCOMPARE( fromCode->defaultValue().toString(), QStringLiteral( "#aabbcc" ) );
QVERIFY( !fromCode->opacityEnabled() );

def->setDefaultValue( QColor( 10, 20, 30 ) );
pythonCode = def->asPythonString();
QCOMPARE( pythonCode, QStringLiteral( "QgsProcessingParameterColor('non_optional', '', defaultValue='#0a141e')" ) );
QCOMPARE( pythonCode, QStringLiteral( "QgsProcessingParameterColor('non_optional', '', opacityEnabled=True, defaultValue=QColor(10, 20, 30))" ) );

def->setOpacityEnabled( false );
pythonCode = def->asPythonString();
QCOMPARE( pythonCode, QStringLiteral( "QgsProcessingParameterColor('non_optional', '', opacityEnabled=False, defaultValue=QColor(10, 20, 30))" ) );
def->setOpacityEnabled( true );

code = def->asScriptCode();
fromCode.reset( dynamic_cast< QgsProcessingParameterColor * >( QgsProcessingParameters::parameterFromScriptCode( code ) ) );
Expand All @@ -6339,27 +6356,31 @@ void TestQgsProcessing::parameterColor()
QCOMPARE( fromCode->description(), QStringLiteral( "non optional" ) );
QCOMPARE( fromCode->flags(), def->flags() );
QCOMPARE( fromCode->defaultValue(), def->defaultValue() );
QVERIFY( fromCode->opacityEnabled() );

fromCode.reset( dynamic_cast< QgsProcessingParameterColor * >( QgsProcessingParameters::parameterFromScriptCode( QStringLiteral( "##non_optional=color 'my val'" ) ) ) );
QVERIFY( fromCode.get() );
QCOMPARE( fromCode->name(), def->name() );
QCOMPARE( fromCode->description(), QStringLiteral( "non optional" ) );
QCOMPARE( fromCode->flags(), def->flags() );
QCOMPARE( fromCode->defaultValue().toString(), QStringLiteral( "my val" ) );
QVERIFY( !fromCode->opacityEnabled() );

fromCode.reset( dynamic_cast< QgsProcessingParameterColor * >( QgsProcessingParameters::parameterFromScriptCode( QStringLiteral( "##non_optional=color \"my val\"" ) ) ) );
fromCode.reset( dynamic_cast< QgsProcessingParameterColor * >( QgsProcessingParameters::parameterFromScriptCode( QStringLiteral( "##non_optional=color withopacity \"my val\"" ) ) ) );
QVERIFY( fromCode.get() );
QCOMPARE( fromCode->name(), def->name() );
QCOMPARE( fromCode->description(), QStringLiteral( "non optional" ) );
QCOMPARE( fromCode->flags(), def->flags() );
QCOMPARE( fromCode->defaultValue().toString(), QStringLiteral( "my val" ) );
QVERIFY( fromCode->opacityEnabled() );

// optional
def.reset( new QgsProcessingParameterColor( "optional", QString(), QString( "#ff00ff" ), true ) );
def.reset( new QgsProcessingParameterColor( "optional", QString(), QString( "#ff00ff" ), false, true ) );
QVERIFY( def->checkValueIsAcceptable( "#ff0000" ) );
QVERIFY( def->checkValueIsAcceptable( QColor( 255, 0, 0 ) ) );
QVERIFY( def->checkValueIsAcceptable( "" ) );
QVERIFY( def->checkValueIsAcceptable( QVariant() ) );
QVERIFY( !def->opacityEnabled() );

params.insert( "optional", QVariant() );
QCOMPARE( QgsProcessingParameters::parameterAsColor( def.get(), params, context ).name(), QString( "#ff00ff" ) );
Expand All @@ -6368,8 +6389,12 @@ void TestQgsProcessing::parameterColor()
params.insert( "optional", QString() ); //empty string should not result in default value
QVERIFY( !QgsProcessingParameters::parameterAsColor( def.get(), params, context ).isValid() );

// not opacity enabled, should be stripped off
params.insert( "optional", QColor( 255, 255, 0, 100 ) );
QCOMPARE( QgsProcessingParameters::parameterAsColor( def.get(), params, context ), QColor( 255, 255, 0 ) );

pythonCode = def->asPythonString();
QCOMPARE( pythonCode, QStringLiteral( "QgsProcessingParameterColor('optional', '', optional=True, defaultValue='#ff00ff')" ) );
QCOMPARE( pythonCode, QStringLiteral( "QgsProcessingParameterColor('optional', '', optional=True, opacityEnabled=False, defaultValue=QColor(255, 0, 255))" ) );
code = def->asScriptCode();
QCOMPARE( code, QStringLiteral( "##optional=optional color #ff00ff" ) );
fromCode.reset( dynamic_cast< QgsProcessingParameterColor * >( QgsProcessingParameters::parameterFromScriptCode( code ) ) );
Expand All @@ -6378,9 +6403,10 @@ void TestQgsProcessing::parameterColor()
QCOMPARE( fromCode->description(), QStringLiteral( "optional" ) );
QCOMPARE( fromCode->flags(), def->flags() );
QCOMPARE( fromCode->defaultValue(), def->defaultValue() );
QVERIFY( !fromCode->opacityEnabled() );

// not optional, valid default!
def.reset( new QgsProcessingParameterColor( "non_optional", QString(), QString( "#ff00ff" ), false ) );
def.reset( new QgsProcessingParameterColor( "non_optional", QString(), QString( "#ff00ff" ), true, false ) );
QVERIFY( def->checkValueIsAcceptable( "#dddddd" ) );
QVERIFY( !def->checkValueIsAcceptable( "" ) );
QVERIFY( def->checkValueIsAcceptable( QVariant() ) ); // should be valid, falls back to valid default
Expand Down

0 comments on commit d96c738

Please sign in to comment.