feat(Expression): Add project_color_object function

Which contrary to project_color doesn't return a string
representation there is no rgb conversion involved
This commit is contained in:
Julien Cabieces 2024-08-29 17:56:38 +02:00 committed by Nyall Dawson
parent c6c755b51a
commit 6c2c9b7750
8 changed files with 100 additions and 9 deletions

View File

@ -130,7 +130,7 @@ qgis_functions = [
"geomFromWKT", "geom_from_gml", "relate", "intersects_bbox", "bbox", "translate", "buffer", "point_on_surface", "reverse",
"exterior_ring", "interior_ring_n", "geometry_n", "bounds", "num_points", "num_interior_rings", "num_rings", "num_geometries",
"bounds_width", "bounds_height", "is_closed", "convex_hull", "sym_difference", "combine", "_union", "geom_to_wkt", "geomToWKT",
"transform", "uuid", "_uuid", "layer_property", "var", "_specialcol_", "project_color"]
"transform", "uuid", "_uuid", "layer_property", "var", "_specialcol_", "project_color", "project_color_object"]
# constants

View File

@ -0,0 +1,15 @@
{
"name": "project_color_object",
"type": "function",
"groups": ["Color"],
"description": "Returns a color from the project's color scheme. Contrary to project_color which returns a color string representation, project_color_object returns a color object.",
"arguments": [{
"arg": "name",
"description": "a color name"
}],
"examples": [{
"expression": "project_color_object('Logo color')",
"returns": "RGBA: 0.08,0.55,0.20,1.00"
}],
"tags": ["scheme", "project", "color"]
}

View File

@ -954,6 +954,7 @@ QgsExpressionContextScope *QgsExpressionContextUtils::notificationScope( const Q
void QgsExpressionContextUtils::registerContextFunctions()
{
QgsExpression::registerFunction( new GetNamedProjectColor( nullptr ) );
QgsExpression::registerFunction( new GetNamedProjectColorObject( nullptr ) );
QgsExpression::registerFunction( new GetSensorData( ) );
QgsExpression::registerFunction( new GetLayoutItemVariables( nullptr ) );
QgsExpression::registerFunction( new GetLayoutMapLayerCredits( nullptr ) );
@ -1430,4 +1431,3 @@ QgsScopedExpressionFunction *LoadLayerFunction::clone() const
return new LoadLayerFunction();
}
///@endcond

View File

@ -2936,6 +2936,7 @@ QgsExpressionContextScope *QgsProject::createExpressionContextScope() const
mProjectScope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "layers" ), layers, true ) );
mProjectScope->addFunction( QStringLiteral( "project_color" ), new GetNamedProjectColor( this ) );
mProjectScope->addFunction( QStringLiteral( "project_color_object" ), new GetNamedProjectColorObject( this ) );
return createExpressionContextScope();
}
@ -5320,11 +5321,10 @@ void QgsProject::cleanFunctionsFromProject()
}
/// @cond PRIVATE
GetNamedProjectColor::GetNamedProjectColor( const QgsProject *project )
: QgsScopedExpressionFunction( QStringLiteral( "project_color" ), 1, QStringLiteral( "Color" ) )
QHash< QString, QColor > loadColorsFromProject( const QgsProject *project )
{
if ( !project )
return;
QHash< QString, QColor > colors;
//build up color list from project. Do this in advance for speed
QStringList colorStrings = project->readListEntry( QStringLiteral( "Palette" ), QStringLiteral( "/Colors" ) );
@ -5342,9 +5342,21 @@ GetNamedProjectColor::GetNamedProjectColor( const QgsProject *project )
label = colorLabels.at( colorIndex );
}
mColors.insert( label.toLower(), color );
colors.insert( label.toLower(), color );
colorIndex++;
}
return colors;
}
GetNamedProjectColor::GetNamedProjectColor( const QgsProject *project )
: QgsScopedExpressionFunction( QStringLiteral( "project_color" ), 1, QStringLiteral( "Color" ) )
{
if ( !project )
return;
mColors = loadColorsFromProject( project );
}
GetNamedProjectColor::GetNamedProjectColor( const QHash<QString, QColor> &colors )
@ -5369,6 +5381,37 @@ QgsScopedExpressionFunction *GetNamedProjectColor::clone() const
return new GetNamedProjectColor( mColors );
}
GetNamedProjectColorObject::GetNamedProjectColorObject( const QgsProject *project )
: QgsScopedExpressionFunction( QStringLiteral( "project_color_object" ), 1, QStringLiteral( "Color" ) )
{
if ( !project )
return;
mColors = loadColorsFromProject( project );
}
GetNamedProjectColorObject::GetNamedProjectColorObject( const QHash<QString, QColor> &colors )
: QgsScopedExpressionFunction( QStringLiteral( "project_color_object" ), 1, QStringLiteral( "Color" ) )
, mColors( colors )
{
}
QVariant GetNamedProjectColorObject::func( const QVariantList &values, const QgsExpressionContext *, QgsExpression *, const QgsExpressionNodeFunction * )
{
const QString colorName = values.at( 0 ).toString().toLower();
if ( mColors.contains( colorName ) )
{
return mColors.value( colorName );
}
else
return QVariant();
}
QgsScopedExpressionFunction *GetNamedProjectColorObject::clone() const
{
return new GetNamedProjectColorObject( mColors );
}
// ----------------
GetSensorData::GetSensorData( const QMap<QString, QgsAbstractSensor::SensorData> &sensorData )

View File

@ -2583,9 +2583,29 @@ class GetNamedProjectColor : public QgsScopedExpressionFunction
private:
QHash< QString, QColor > mColors;
};
class GetNamedProjectColorObject : public QgsScopedExpressionFunction
{
public:
GetNamedProjectColorObject( const QgsProject *project );
/**
* Optimized constructor for GetNamedProjectColor when a list of map is already available
* and does not need to be read from a project.
*/
GetNamedProjectColorObject( const QHash< QString, QColor > &colors );
QVariant func( const QVariantList &values, const QgsExpressionContext *, QgsExpression *, const QgsExpressionNodeFunction * ) override;
QgsScopedExpressionFunction *clone() const override;
private:
QHash< QString, QColor > mColors;
};
class GetSensorData : public QgsScopedExpressionFunction
{
public:

View File

@ -505,7 +505,7 @@ QSet<QString> QgsProperty::referencedFields( const QgsExpressionContext &context
bool QgsProperty::isProjectColor() const
{
const thread_local QRegularExpression rx( QStringLiteral( "^project_color\\('.*'\\)$" ) );
const thread_local QRegularExpression rx( QStringLiteral( "^project_color(_object|)\\('.*'\\)$" ) );
return d->type == Qgis::PropertyType::Expression && !d->expressionString.isEmpty()
&& rx.match( d->expressionString ).hasMatch();
}

View File

@ -823,6 +823,15 @@ void TestQgsExpressionContext::projectScope()
QCOMPARE( expProjectColorCaseInsensitive.evaluate( &contextColors ).toString(), QString( "30,60,20" ) );
QgsExpression badProjectColor( QStringLiteral( "project_color('dusk falls in san juan del sur')" ) );
QCOMPARE( badProjectColor.evaluate( &contextColors ), QVariant() );
QgsExpression expProjectColorObject( QStringLiteral( "project_color_object('murky depths of hades')" ) );
QCOMPARE( expProjectColorObject.evaluate( &contextColors ), QVariant( QColor::fromRgb( 30, 60, 20 ) ) );
//matching color names should be case insensitive
QgsExpression expProjectColorObjectCaseInsensitive( QStringLiteral( "project_color_object('Murky Depths of hades')" ) );
QCOMPARE( expProjectColorObjectCaseInsensitive.evaluate( &contextColors ), QVariant( QColor::fromRgb( 30, 60, 20 ) ) );
QgsExpression badProjectColorObject( QStringLiteral( "project_color_object('dusk falls in san juan del sur')" ) );
QCOMPARE( badProjectColorObject.evaluate( &contextColors ), QVariant() );
}
void TestQgsExpressionContext::layerScope()

View File

@ -1946,6 +1946,10 @@ void TestQgsProperty::isProjectColor()
QVERIFY( p.isProjectColor() );
p = QgsProperty::fromExpression( QStringLiteral( "project_color('burnt pineapple Skin 76')" ), true );
QVERIFY( p.isProjectColor() );
p = QgsProperty::fromExpression( QStringLiteral( "project_color_object('mine')" ), true );
QVERIFY( p.isProjectColor() );
p = QgsProperty::fromExpression( QStringLiteral( "project_color_object('burnt pineapple Skin 76')" ), true );
QVERIFY( p.isProjectColor() );
p.setActive( false );
QVERIFY( p.isProjectColor() );
}