Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
Allow expression functions to appear in multiple groups (fix #15682)
Now functions which make sense for multiple contexts (eg length,
to_date) can appear in more than one group.

(cherry-picked from 0c4bf94)
  • Loading branch information
nyalldawson committed Oct 12, 2016
1 parent d558077 commit 6fc9712
Show file tree
Hide file tree
Showing 5 changed files with 166 additions and 27 deletions.
43 changes: 42 additions & 1 deletion python/core/qgsexpression.sip
Expand Up @@ -376,6 +376,19 @@ class QgsExpression
bool handlesNull = false,
bool isContextual = false );

/** Constructor for function which uses unnamed parameters and group list
* @note added in QGIS 3.0
*/
Function( const QString& fnname,
int params,
const QStringList& groups,
const QString& helpText = QString(),
bool usesGeometry = false,
const QStringList& referencedColumns = QStringList(),
bool lazyEval = false,
bool handlesNull = false,
bool isContextual = false );

/** Constructor for function which uses named parameter list.
* @note added in QGIS 2.16
*/
Expand All @@ -389,6 +402,19 @@ class QgsExpression
bool handlesNull = false,
bool isContextual = false );

/** Constructor for function which uses named parameter list and group list.
* @note added in QGIS 3.0
*/
Function( const QString& fnname,
const QgsExpression::ParameterList& params,
const QStringList& groups,
const QString& helpText = QString(),
bool usesGeometry = false,
const QStringList& referencedColumns = QStringList(),
bool lazyEval = false,
bool handlesNull = false,
bool isContextual = false );

virtual ~Function();

/** The name of the function. */
Expand Down Expand Up @@ -427,8 +453,23 @@ class QgsExpression
*/
bool isContextual() const;

/** The group the function belongs to. */
/** Returns true if the function is deprecated and should not be presented as a valid option
* to users in expression builders.
* @note added in QGIS 3.0
*/
virtual bool isDeprecated() const;

/** Returns the first group which the function belongs to.
* @note consider using groups() instead, as some functions naturally belong in multiple groups
*/
QString group() const;

/** Returns a list of the groups the function belongs to.
* @note added in QGIS 3.0
* @see group()
*/
QStringList groups() const;

/** The help text for the function. */
const QString helptext() const;

Expand Down
22 changes: 11 additions & 11 deletions src/core/qgsexpression.cpp
Expand Up @@ -3298,7 +3298,7 @@ const QList<QgsExpression::Function*>& QgsExpression::Functions()
<< new StaticFunction( "sqrt", ParameterList() << Parameter( "value" ), fcnSqrt, "Math" )
<< new StaticFunction( "radians", ParameterList() << Parameter( "degrees" ), fcnRadians, "Math" )
<< new StaticFunction( "degrees", ParameterList() << Parameter( "radians" ), fcnDegrees, "Math" )
<< new StaticFunction( "azimuth", ParameterList() << Parameter( "point_a" ) << Parameter( "point_b" ), fcnAzimuth, "Math" )
<< new StaticFunction( "azimuth", ParameterList() << Parameter( "point_a" ) << Parameter( "point_b" ), fcnAzimuth, QStringList() << "Math" << "GeometryGroup" )
<< new StaticFunction( "project", ParameterList() << Parameter( "point" ) << Parameter( "distance" ) << Parameter( "bearing" ), fcnProject, "GeometryGroup" )
<< new StaticFunction( "abs", ParameterList() << Parameter( "value" ), fcnAbs, "Math" )
<< new StaticFunction( "cos", ParameterList() << Parameter( "angle" ), fcnCos, "Math" )
Expand All @@ -3323,13 +3323,13 @@ const QList<QgsExpression::Function*>& QgsExpression::Functions()
<< new StaticFunction( "floor", 1, fcnFloor, "Math" )
<< new StaticFunction( "ceil", 1, fcnCeil, "Math" )
<< new StaticFunction( "pi", 0, fcnPi, "Math", QString(), false, QStringList(), false, QStringList() << "$pi" )
<< new StaticFunction( "to_int", 1, fcnToInt, "Conversions", QString(), false, QStringList(), false, QStringList() << "toint" )
<< new StaticFunction( "to_real", 1, fcnToReal, "Conversions", QString(), false, QStringList(), false, QStringList() << "toreal" )
<< new StaticFunction( "to_string", 1, fcnToString, "Conversions", QString(), false, QStringList(), false, QStringList() << "tostring" )
<< new StaticFunction( "to_datetime", 1, fcnToDateTime, "Conversions", QString(), false, QStringList(), false, QStringList() << "todatetime" )
<< new StaticFunction( "to_date", 1, fcnToDate, "Conversions", QString(), false, QStringList(), false, QStringList() << "todate" )
<< new StaticFunction( "to_time", 1, fcnToTime, "Conversions", QString(), false, QStringList(), false, QStringList() << "totime" )
<< new StaticFunction( "to_interval", 1, fcnToInterval, "Conversions", QString(), false, QStringList(), false, QStringList() << "tointerval" )
<< new StaticFunction( "to_int", ParameterList() << Parameter( "value" ), fcnToInt, "Conversions", QString(), false, QStringList(), false, QStringList() << "toint" )
<< new StaticFunction( "to_real", ParameterList() << Parameter( "value" ), fcnToReal, "Conversions", QString(), false, QStringList(), false, QStringList() << "toreal" )
<< new StaticFunction( "to_string", ParameterList() << Parameter( "value" ), fcnToString, QStringList() << "Conversions" << "String", QString(), false, QStringList(), false, QStringList() << "tostring" )
<< new StaticFunction( "to_datetime", ParameterList() << Parameter( "value" ), fcnToDateTime, QStringList() << "Conversions" << "Date and Time", QString(), false, QStringList(), false, QStringList() << "todatetime" )
<< new StaticFunction( "to_date", ParameterList() << Parameter( "value" ), fcnToDate, QStringList() << "Conversions" << "Date and Time", QString(), false, QStringList(), false, QStringList() << "todate" )
<< new StaticFunction( "to_time", ParameterList() << Parameter( "value" ), fcnToTime, QStringList() << "Conversions" << "Date and Time", QString(), false, QStringList(), false, QStringList() << "totime" )
<< new StaticFunction( "to_interval", ParameterList() << Parameter( "value" ), fcnToInterval, QStringList() << "Conversions" << "Date and Time", QString(), false, QStringList(), false, QStringList() << "tointerval" )
<< new StaticFunction( "coalesce", -1, fcnCoalesce, "Conditionals", QString(), false, QStringList(), false, QStringList(), true )
<< new StaticFunction( "if", 3, fcnIf, "Conditionals", QString(), False, QStringList(), true )
<< new StaticFunction( "aggregate", ParameterList() << Parameter( "layer" ) << Parameter( "aggregate" ) << Parameter( "expression" )
Expand All @@ -3356,7 +3356,7 @@ const QList<QgsExpression::Function*>& QgsExpression::Functions()
<< new StaticFunction( "max_length", aggParams, fcnAggregateMaxLength, "Aggregates", QString(), False, QStringList(), true )
<< new StaticFunction( "concatenate", aggParams << Parameter( "concatenator", true ), fcnAggregateStringConcat, "Aggregates", QString(), False, QStringList(), true )

<< new StaticFunction( "regexp_match", 2, fcnRegexpMatch, "Conditionals" )
<< new StaticFunction( "regexp_match", ParameterList() << Parameter( "string" ) << Parameter( "regex" ), fcnRegexpMatch, QStringList() << "Conditionals" << "String" )
<< new StaticFunction( "now", 0, fcnNow, "Date and Time", QString(), false, QStringList(), false, QStringList() << "$now" )
<< new StaticFunction( "age", 2, fcnAge, "Date and Time" )
<< new StaticFunction( "year", 1, fcnYear, "Date and Time" )
Expand All @@ -3377,7 +3377,7 @@ const QList<QgsExpression::Function*>& QgsExpression::Functions()
<< new StaticFunction( "soundex", 1, fcnSoundex, "Fuzzy Matching" )
<< new StaticFunction( "char", 1, fcnChar, "String" )
<< new StaticFunction( "wordwrap", ParameterList() << Parameter( "text" ) << Parameter( "length" ) << Parameter( "delimiter", true, " " ), fcnWordwrap, "String" )
<< new StaticFunction( "length", 1, fcnLength, "String" )
<< new StaticFunction( "length", ParameterList() << Parameter( "text", true, "" ), fcnLength, QStringList() << "String" << "GeometryGroup" )
<< new StaticFunction( "replace", 3, fcnReplace, "String" )
<< new StaticFunction( "regexp_replace", 3, fcnRegexpReplace, "String" )
<< new StaticFunction( "regexp_substr", 2, fcnRegexpSubstr, "String" )
Expand All @@ -3390,7 +3390,7 @@ const QList<QgsExpression::Function*>& QgsExpression::Functions()
<< new StaticFunction( "lpad", 3, fcnLPad, "String" )
<< new StaticFunction( "format", -1, fcnFormatString, "String" )
<< new StaticFunction( "format_number", 2, fcnFormatNumber, "String" )
<< new StaticFunction( "format_date", 2, fcnFormatDate, "String" )
<< new StaticFunction( "format_date", ParameterList() << Parameter( "date" ) << Parameter( "format" ), fcnFormatDate, QStringList() << "String" << "Date and Time" )
<< new StaticFunction( "color_rgb", 3, fcnColorRgb, "Color" )
<< new StaticFunction( "color_rgba", 4, fncColorRgba, "Color" )
<< new StaticFunction( "ramp_color", 2, fcnRampColor, "Color" )
Expand Down
93 changes: 88 additions & 5 deletions src/core/qgsexpression.h
Expand Up @@ -530,7 +530,31 @@ class CORE_EXPORT QgsExpression
: mName( fnname )
, mParams( params )
, mUsesGeometry( usesGeometry )
, mGroup( group )
, mGroups( group.isEmpty() ? QStringList() : QStringList() << group )
, mHelpText( helpText )
, mReferencedColumns( referencedColumns )
, mLazyEval( lazyEval )
, mHandlesNull( handlesNull )
, mIsContextual( isContextual )
{
}

/** Constructor for function which uses unnamed parameters and group list
* @note added in QGIS 3.0
*/
Function( const QString& fnname,
int params,
const QStringList& groups,
const QString& helpText = QString(),
bool usesGeometry = false,
const QStringList& referencedColumns = QStringList(),
bool lazyEval = false,
bool handlesNull = false,
bool isContextual = false )
: mName( fnname )
, mParams( params )
, mUsesGeometry( usesGeometry )
, mGroups( groups )
, mHelpText( helpText )
, mReferencedColumns( referencedColumns )
, mLazyEval( lazyEval )
Expand All @@ -555,14 +579,39 @@ class CORE_EXPORT QgsExpression
, mParams( 0 )
, mParameterList( params )
, mUsesGeometry( usesGeometry )
, mGroup( group )
, mGroups( group.isEmpty() ? QStringList() : QStringList() << group )
, mHelpText( helpText )
, mReferencedColumns( referencedColumns )
, mLazyEval( lazyEval )
, mHandlesNull( handlesNull )
, mIsContextual( isContextual )
{}

/** Constructor for function which uses named parameter list and group list.
* @note added in QGIS 3.0
*/
Function( const QString& fnname,
const ParameterList& params,
const QStringList& groups,
const QString& helpText = QString(),
bool usesGeometry = false,
const QStringList& referencedColumns = QStringList(),
bool lazyEval = false,
bool handlesNull = false,
bool isContextual = false )
: mName( fnname )
, mParams( 0 )
, mParameterList( params )
, mUsesGeometry( usesGeometry )
, mGroups( groups )
, mHelpText( helpText )
, mReferencedColumns( referencedColumns )
, mLazyEval( lazyEval )
, mHandlesNull( handlesNull )
, mIsContextual( isContextual )
{}


virtual ~Function() {}

/** The name of the function. */
Expand Down Expand Up @@ -614,8 +663,23 @@ class CORE_EXPORT QgsExpression
*/
bool isContextual() const { return mIsContextual; }

/** The group the function belongs to. */
QString group() const { return mGroup; }
/** Returns true if the function is deprecated and should not be presented as a valid option
* to users in expression builders.
* @note added in QGIS 3.0
*/
virtual bool isDeprecated() const { return mGroups.isEmpty() ? false : mGroups.contains( "deprecated" ); }

/** Returns the first group which the function belongs to.
* @note consider using groups() instead, as some functions naturally belong in multiple groups
*/
QString group() const { return mGroups.isEmpty() ? QString() : mGroups.at( 0 ); }

/** Returns a list of the groups the function belongs to.
* @note added in QGIS 3.0
* @see group()
*/
QStringList groups() const { return mGroups; }

/** The help text for the function. */
//TODO QGIS 3.0 - rename to helpText()
const QString helptext() const { return mHelpText.isEmpty() ? QgsExpression::helptext( mName ) : mHelpText; }
Expand Down Expand Up @@ -648,7 +712,7 @@ class CORE_EXPORT QgsExpression
int mParams;
ParameterList mParameterList;
bool mUsesGeometry;
QString mGroup;
QStringList mGroups;
QString mHelpText;
QStringList mReferencedColumns;
bool mLazyEval;
Expand Down Expand Up @@ -718,6 +782,25 @@ class CORE_EXPORT QgsExpression
, mAliases( aliases )
{}

/** Static function for evaluation against a QgsExpressionContext, using a named list of parameter values and list
* of groups.
*/
StaticFunction( const QString& fnname,
const ParameterList& params,
FcnEvalContext fcn,
const QStringList& groups,
const QString& helpText = QString(),
bool usesGeometry = false,
const QStringList& referencedColumns = QStringList(),
bool lazyEval = false,
const QStringList& aliases = QStringList(),
bool handlesNull = false )
: Function( fnname, params, groups, helpText, usesGeometry, referencedColumns, lazyEval, handlesNull )
, mFnc( nullptr )
, mContextFnc( fcn )
, mAliases( aliases )
{}

//! @deprecated use QgsExpressionContext variant instead
Q_DECL_DEPRECATED virtual QVariant func( const QVariantList& values, const QgsFeature* f, QgsExpression* parent ) override;

Expand Down
21 changes: 11 additions & 10 deletions src/gui/qgsexpressionbuilderwidget.cpp
Expand Up @@ -477,7 +477,7 @@ void QgsExpressionBuilderWidget::updateFunctionTree()
QString name = func->name();
if ( name.startsWith( '_' ) ) // do not display private functions
continue;
if ( func->group() == "deprecated" ) // don't show deprecated functions
if ( func->isDeprecated() ) // don't show deprecated functions
continue;
if ( func->isContextual() )
{
Expand All @@ -489,14 +489,7 @@ void QgsExpressionBuilderWidget::updateFunctionTree()
name += '(';
else if ( !name.startsWith( '$' ) )
name += "()";
registerItem( func->group(), func->name(), ' ' + name + ' ', func->helptext() );
}

QList<QgsExpression::Function*> specials = QgsExpression::specialColumns();
for ( int i = 0; i < specials.size(); ++i )
{
QString name = specials[i]->name();
registerItem( specials[i]->group(), name, ' ' + name + ' ' );
registerItemForAllGroups( func->groups(), func->name(), ' ' + name + ' ', func->helptext() );
}

loadExpressionContext();
Expand Down Expand Up @@ -619,7 +612,15 @@ void QgsExpressionBuilderWidget::loadExpressionContext()
continue;
if ( func->params() != 0 )
name += '(';
registerItem( func->group(), func->name(), ' ' + name + ' ', func->helptext() );
registerItemForAllGroups( func->groups(), func->name(), ' ' + name + ' ', func->helptext() );
}
}

void QgsExpressionBuilderWidget::registerItemForAllGroups( const QStringList& groups, const QString& label, const QString& expressionText, const QString& helpText, QgsExpressionItem::ItemType type, bool highlightedItem, int sortOrder )
{
Q_FOREACH ( const QString& group, groups )
{
registerItem( group, label, expressionText, helpText, type, highlightedItem, sortOrder );
}
}

Expand Down
14 changes: 14 additions & 0 deletions src/gui/qgsexpressionbuilderwidget.h
Expand Up @@ -278,6 +278,20 @@ class GUI_EXPORT QgsExpressionBuilderWidget : public QWidget, private Ui::QgsExp

void loadExpressionContext();

/** Registers a node item for the expression builder, adding multiple items when the function exists in multiple groups
* @param groups The groups the item will be show in the tree view. If a group doesn't exist it will be created.
* @param label The label that is show to the user for the item in the tree.
* @param expressionText The text that is inserted into the expression area when the user double clicks on the item.
* @param helpText The help text that the user will see when item is selected.
* @param type The type of the expression item.
* @param highlightedItem set to true to make the item highlighted, which inserts a bold copy of the item at the top level
* @param sortOrder sort ranking for item
*/
void registerItemForAllGroups( const QStringList& groups, const QString& label, const QString& expressionText,
const QString& helpText = "",
QgsExpressionItem::ItemType type = QgsExpressionItem::ExpressionNode,
bool highlightedItem = false, int sortOrder = 1 );

bool mAutoSave;
QString mFunctionsPath;
QgsVectorLayer *mLayer;
Expand Down

0 comments on commit 6fc9712

Please sign in to comment.