Skip to content

Commit

Permalink
Allow expression functions to appear in multiple groups (fix #15682)
Browse files Browse the repository at this point in the history
Now functions which make sense for multiple contexts (eg length,
to_date) can appear in more than one group.
  • Loading branch information
nyalldawson committed Oct 10, 2016
1 parent 3b1dfa7 commit 0c4bf94
Show file tree
Hide file tree
Showing 5 changed files with 163 additions and 20 deletions.
43 changes: 42 additions & 1 deletion python/core/qgsexpression.sip
Expand Up @@ -306,6 +306,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 QSet<QString>& referencedColumns = QSet<QString>(),
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 @@ -319,6 +332,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 QSet<QString>& referencedColumns = QSet<QString>(),
bool lazyEval = false,
bool handlesNull = false,
bool isContextual = false );

virtual ~Function();

/** The name of the function. */
Expand Down Expand Up @@ -357,8 +383,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 @@ -3403,7 +3403,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 @@ -3428,13 +3428,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, QSet<QString>(), false, QStringList() << "$pi" )
<< new StaticFunction( "to_int", 1, fcnToInt, "Conversions", QString(), false, QSet<QString>(), false, QStringList() << "toint" )
<< new StaticFunction( "to_real", 1, fcnToReal, "Conversions", QString(), false, QSet<QString>(), false, QStringList() << "toreal" )
<< new StaticFunction( "to_string", 1, fcnToString, "Conversions", QString(), false, QSet<QString>(), false, QStringList() << "tostring" )
<< new StaticFunction( "to_datetime", 1, fcnToDateTime, "Conversions", QString(), false, QSet<QString>(), false, QStringList() << "todatetime" )
<< new StaticFunction( "to_date", 1, fcnToDate, "Conversions", QString(), false, QSet<QString>(), false, QStringList() << "todate" )
<< new StaticFunction( "to_time", 1, fcnToTime, "Conversions", QString(), false, QSet<QString>(), false, QStringList() << "totime" )
<< new StaticFunction( "to_interval", 1, fcnToInterval, "Conversions", QString(), false, QSet<QString>(), false, QStringList() << "tointerval" )
<< new StaticFunction( "to_int", ParameterList() << Parameter( "value" ), fcnToInt, "Conversions", QString(), false, QSet<QString>(), false, QStringList() << "toint" )
<< new StaticFunction( "to_real", ParameterList() << Parameter( "value" ), fcnToReal, "Conversions", QString(), false, QSet<QString>(), false, QStringList() << "toreal" )
<< new StaticFunction( "to_string", ParameterList() << Parameter( "value" ), fcnToString, QStringList() << "Conversions" << "String", QString(), false, QSet<QString>(), false, QStringList() << "tostring" )
<< new StaticFunction( "to_datetime", ParameterList() << Parameter( "value" ), fcnToDateTime, QStringList() << "Conversions" << "Date and Time", QString(), false, QSet<QString>(), false, QStringList() << "todatetime" )
<< new StaticFunction( "to_date", ParameterList() << Parameter( "value" ), fcnToDate, QStringList() << "Conversions" << "Date and Time", QString(), false, QSet<QString>(), false, QStringList() << "todate" )
<< new StaticFunction( "to_time", ParameterList() << Parameter( "value" ), fcnToTime, QStringList() << "Conversions" << "Date and Time", QString(), false, QSet<QString>(), false, QStringList() << "totime" )
<< new StaticFunction( "to_interval", ParameterList() << Parameter( "value" ), fcnToInterval, QStringList() << "Conversions" << "Date and Time", QString(), false, QSet<QString>(), false, QStringList() << "tointerval" )
<< new StaticFunction( "coalesce", -1, fcnCoalesce, "Conditionals", QString(), false, QSet<QString>(), false, QStringList(), true )
<< new StaticFunction( "if", 3, fcnIf, "Conditionals", QString(), False, QSet<QString>(), true )
<< new StaticFunction( "aggregate", ParameterList() << Parameter( "layer" ) << Parameter( "aggregate" ) << Parameter( "expression" )
Expand Down Expand Up @@ -3462,7 +3462,7 @@ const QList<QgsExpression::Function*>& QgsExpression::Functions()
<< new StaticFunction( "collect", aggParams, fcnAggregateCollectGeometry, "Aggregates", QString(), False, QSet<QString>(), true )
<< new StaticFunction( "concatenate", aggParams << Parameter( "concatenator", true ), fcnAggregateStringConcat, "Aggregates", QString(), False, QSet<QString>(), 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, QSet<QString>(), false, QStringList() << "$now" )
<< new StaticFunction( "age", 2, fcnAge, "Date and Time" )
<< new StaticFunction( "year", 1, fcnYear, "Date and Time" )
Expand All @@ -3483,7 +3483,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 @@ -3496,7 +3496,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
90 changes: 85 additions & 5 deletions src/core/qgsexpression.h
Expand Up @@ -458,7 +458,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 QSet<QString>& referencedColumns = QSet<QString>(),
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 @@ -483,7 +507,31 @@ 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 QSet<QString>& referencedColumns = QSet<QString>(),
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 )
Expand Down Expand Up @@ -541,8 +589,22 @@ 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. */
const QString helpText() const { return mHelpText.isEmpty() ? QgsExpression::helpText( mName ) : mHelpText; }
Expand All @@ -564,7 +626,7 @@ class CORE_EXPORT QgsExpression
int mParams;
ParameterList mParameterList;
bool mUsesGeometry;
QString mGroup;
QStringList mGroups;
QString mHelpText;
QSet<QString> mReferencedColumns;
bool mLazyEval;
Expand Down Expand Up @@ -614,6 +676,24 @@ 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,
FcnEval fcn,
const QStringList& groups,
const QString& helpText = QString(),
bool usesGeometry = false,
const QSet<QString>& referencedColumns = QSet<QString>(),
bool lazyEval = false,
const QStringList& aliases = QStringList(),
bool handlesNull = false )
: Function( fnname, params, groups, helpText, usesGeometry, referencedColumns, lazyEval, handlesNull )
, mFnc( fcn )
, mAliases( aliases )
{}

virtual ~StaticFunction() {}

/** Returns result of evaluating the function.
Expand Down
14 changes: 11 additions & 3 deletions src/gui/qgsexpressionbuilderwidget.cpp
Expand Up @@ -479,7 +479,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 @@ -491,7 +491,7 @@ void QgsExpressionBuilderWidget::updateFunctionTree()
name += '(';
else if ( !name.startsWith( '$' ) )
name += "()";
registerItem( func->group(), func->name(), ' ' + name + ' ', func->helpText() );
registerItemForAllGroups( func->groups(), func->name(), ' ' + name + ' ', func->helpText() );
}

loadExpressionContext();
Expand Down Expand Up @@ -614,7 +614,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 @@ -281,6 +281,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 0c4bf94

Please sign in to comment.