Allow creation of color parameters with no opacity control

This commit is contained in:
Nyall Dawson 2019-06-30 16:52:24 +10:00
parent 8fc63bff88
commit d96c7386b1
7 changed files with 170 additions and 18 deletions

View File

@ -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
@ -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();
@ -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

View File

@ -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 );
@ -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 )
@ -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 )
{
}
@ -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 );
@ -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();
}
@ -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;
@ -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( '\'' ) )
@ -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 );
}

View File

@ -2860,7 +2860,10 @@ 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
{
@ -2868,8 +2871,11 @@ class CORE_EXPORT QgsProcessingParameterColor : public QgsProcessingParameterDef
/**
* 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 );
/**
@ -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

View File

@ -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, [ = ]
{

View File

@ -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" ) );
@ -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" );
@ -6311,15 +6320,17 @@ 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() );
@ -6327,10 +6338,16 @@ void TestQgsProcessing::parameterColor()
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 ) ) );
@ -6339,6 +6356,7 @@ 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() );
@ -6346,20 +6364,23 @@ void TestQgsProcessing::parameterColor()
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" ) );
@ -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 ) ) );
@ -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

View File

@ -3034,6 +3034,7 @@ void TestProcessingGui::testColorWrapper()
QCOMPARE( wrapper.widgetValue().value< QColor >().name(), QStringLiteral( "#ff0000" ) );
QCOMPARE( static_cast< QgsColorButton * >( wrapper.wrappedWidget() )->color(), QColor( 255, 0, 0 ) );
QVERIFY( !static_cast< QgsColorButton * >( wrapper.wrappedWidget() )->showNull() );
QVERIFY( static_cast< QgsColorButton * >( wrapper.wrappedWidget() )->allowOpacity() );
wrapper.setWidgetValue( QColor(), context );
QCOMPARE( spy.count(), 2 );
QVERIFY( !wrapper.widgetValue().value< QColor >().isValid() );
@ -3056,10 +3057,14 @@ void TestProcessingGui::testColorWrapper()
static_cast< QgsColorButton * >( wrapper.wrappedWidget() )->setColor( QColor( 0, 255, 0 ) );
QCOMPARE( spy.count(), 3 );
// with opacity
wrapper.setWidgetValue( QColor( 255, 0, 0, 100 ), context );
QCOMPARE( wrapper.widgetValue().value< QColor >(), QColor( 255, 0, 0, 100 ) );
delete w;
// with null
QgsProcessingParameterColor param2( QStringLiteral( "c2" ), QStringLiteral( "c2" ), QColor( 10, 20, 30 ), true );
QgsProcessingParameterColor param2( QStringLiteral( "c2" ), QStringLiteral( "c2" ), QColor( 10, 20, 30 ), true, true );
QgsProcessingColorWidgetWrapper wrapper2( &param2, type );
w = wrapper2.createWrappedWidget( context );
@ -3070,6 +3075,13 @@ void TestProcessingGui::testColorWrapper()
wrapper2.setWidgetValue( QColor( 255, 0, 255 ), context );
QCOMPARE( wrapper2.widgetValue().value< QColor >().name(), QStringLiteral( "#ff00ff" ) );
// no opacity
QgsProcessingParameterColor param3( QStringLiteral( "c2" ), QStringLiteral( "c2" ), QColor( 10, 20, 30 ), false, true );
QgsProcessingColorWidgetWrapper wrapper3( &param3, type );
w = wrapper3.createWrappedWidget( context );
wrapper3.setWidgetValue( QColor( 255, 0, 0, 100 ), context );
QCOMPARE( wrapper3.widgetValue().value< QColor >(), QColor( 255, 0, 0 ) );
};
// standard wrapper

Binary file not shown.