Skip to content

Commit

Permalink
Merge pull request #34827 from elpaso/parent-form-current-value-2
Browse files Browse the repository at this point in the history
[feature] Get current parent form values in child forms
  • Loading branch information
elpaso committed Mar 5, 2020
2 parents 0c3f233 + 60e9ed4 commit 3246a92
Show file tree
Hide file tree
Showing 34 changed files with 547 additions and 26 deletions.
Expand Up @@ -33,11 +33,20 @@ For instance, QGIS version numbers and variables specified through QGIS options.

static QgsExpressionContextScope *formScope( const QgsFeature &formFeature = QgsFeature( ), const QString &formMode = QString() ) /Factory/;
%Docstring
Creates a new scope which contains functions and variables from the current attribute form/table ``feature``.
Creates a new scope which contains functions and variables from the current attribute form/table ``formFeature``.
The variables and values in this scope will reflect the current state of the form/row being edited.
The ``formMode`` (SingleEditMode etc.) is passed as text

.. versionadded:: 3.2
%End

static QgsExpressionContextScope *parentFormScope( const QgsFeature &formFeature = QgsFeature( ), const QString &formMode = QString() ) /Factory/;
%Docstring
Creates a new scope which contains functions and variables from the current parent attribute form/table ``formFeature``.
The variables and values in this scope will reflect the current state of the parent form/row being edited.
The ``formMode`` (SingleEditMode etc.) is passed as text

.. versionadded:: 3.14
%End

static void setGlobalVariable( const QString &name, const QVariant &value );
Expand Down
Expand Up @@ -64,14 +64,15 @@ Utility to convert a list or a string representation of an (hstore style: {1,2..
.. versionadded:: 3.2
%End

static QgsValueRelationFieldFormatter::ValueRelationCache createCache( const QVariantMap &config, const QgsFeature &formFeature = QgsFeature() );
static QgsValueRelationFieldFormatter::ValueRelationCache createCache( const QVariantMap &config, const QgsFeature &formFeature = QgsFeature(), const QgsFeature &parentFormFeature = QgsFeature() );
%Docstring
Create a cache for a value relation field.
This can be used to keep the value map in the local memory
if doing multiple lookups in a loop.

:param config: The widget configuration
:param formFeature: The feature currently being edited with current attribute values
:param parentFormFeature: For embedded forms only, the feature currently being edited in the parent form with current attribute values

:return: A kvp list of values for the widget

Expand Down Expand Up @@ -112,9 +113,45 @@ Returns a list of variables required by the form context ``expression``
.. versionadded:: 3.2
%End

static bool expressionIsUsable( const QString &expression, const QgsFeature &feature );
static bool expressionRequiresParentFormScope( const QString &expression );
%Docstring
Check whether the ``feature`` has all values required by the ``expression``
Check if the ``expression`` requires a parent form scope (i.e. if it uses fields
or geometry of the parent form's currently edited feature).

:param expression: The widget's filter expression

:return: ``True`` if the expression requires a parent form scope

.. versionadded:: 3.14
%End

static QSet<QString> expressionParentFormAttributes( const QString &expression );
%Docstring
Returns a list of attributes required by the parent form's form context ``expression``

:param expression: Form filter expression

:return: list of parent attributes required by the expression

.. versionadded:: 3.14
%End

static QSet<QString> expressionParentFormVariables( const QString &expression );
%Docstring
Returns a list of variables required by the parent form's form context ``expression``

:param expression: Form filter expression

:return: list of parent variables required by the expression

.. versionadded:: 3.14
%End


static bool expressionIsUsable( const QString &expression, const QgsFeature &feature, const QgsFeature &parentFeature = QgsFeature() );
%Docstring
Check whether the ``feature`` has all values required by the ``expression``,
optionally checks for ``parentFeature``

:return: ``True`` if the expression can be used

Expand Down
Expand Up @@ -69,6 +69,7 @@ Returns the selected features in the attribute table in table sorted order.
.. versionadded:: 3.4
%End


protected:

virtual void mousePressEvent( QMouseEvent *event );
Expand Down
10 changes: 10 additions & 0 deletions python/gui/auto_generated/attributetable/qgsdualview.sip.in
Expand Up @@ -232,6 +232,16 @@ Copy the content of the selected cell in the clipboard.
Cancel the progress dialog (if any)

.. versionadded:: 3.0
%End

void parentFormValueChanged( const QString &attribute, const QVariant &value );
%Docstring
Called in embedded forms when an ``attribute`` ``value`` in the parent form has changed.

Notify the form widgets that something has changed in case they
have filter expression that depend on the parent form scope.

.. versionadded:: 3.14
%End

signals:
Expand Down
Expand Up @@ -302,6 +302,19 @@ to reflect the new values.
void emitValueChanged();
%Docstring
Will call the value() method to determine the emitted value
%End

virtual void parentFormValueChanged( const QString &attribute, const QVariant &value );
%Docstring
Is called in embedded form widgets when an ``attribute`` ``value`` in
the parent form has changed.

The default implementations does nothing.
Subclasses should reimplement this method to notify the form widgets
that something has changed in case they have filter expressions that
depend on the parent form scope.

.. versionadded:: 3.14
%End

protected:
Expand Down
Expand Up @@ -88,6 +88,21 @@ The relation for which this wrapper is created.
.. versionadded:: 3.0
%End

void widgetValueChanged( const QString &attribute, const QVariant &newValue, bool attributeChanged );
%Docstring
Will be called when a value in the current edited form or table row
changes

Forward the signal to the embedded form

:param attribute: The name of the attribute that changed.
:param newValue: The new value of the attribute.
:param attributeChanged: If ``True``, it corresponds to an actual change of the feature attribute

.. versionadded:: 3.14
%End


protected:
virtual QWidget *createWidget( QWidget *parent );

Expand Down
18 changes: 18 additions & 0 deletions python/gui/auto_generated/qgsattributeeditorcontext.sip.in
Expand Up @@ -235,6 +235,24 @@ Set current ``feature`` for the currently edited form or table row
.. seealso:: :py:func:`formFeature`

.. versionadded:: 3.2
%End

QgsFeature parentFormFeature() const;
%Docstring
Returns the feature of the currently edited parent form in its actual state

.. seealso:: :py:func:`setParentFormFeature`

.. versionadded:: 3.14
%End

void setParentFormFeature( const QgsFeature &feature );
%Docstring
Sets the ``feature`` of the currently edited parent form

.. seealso:: :py:func:`parentFormFeature`

.. versionadded:: 3.14
%End

Mode attributeFormMode() const;
Expand Down
12 changes: 12 additions & 0 deletions python/gui/auto_generated/qgsattributeform.sip.in
Expand Up @@ -280,6 +280,18 @@ Resets the search/filter form values.
reload current feature
%End

void parentFormValueChanged( const QString &attribute, const QVariant &newValue );
%Docstring
Is called in embedded forms when an ``attribute`` value in the parent form
has changed to ``newValue``.

Notify the form widgets that something has changed in case they
have filter expressions that depend on the parent form scope.

.. versionadded:: 3.14
%End


};


Expand Down
26 changes: 25 additions & 1 deletion python/gui/auto_generated/qgsrelationeditorwidget.sip.in
Expand Up @@ -51,6 +51,9 @@ Gets the view mode for the dual view
%End

void setRelationFeature( const QgsRelation &relation, const QgsFeature &feature );
%Docstring
Sets the ``relation`` and the ``feature``
%End

void setRelations( const QgsRelation &relation, const QgsRelation &nmrelation );
%Docstring
Expand All @@ -63,9 +66,15 @@ inserting and deleting entries on the intermediate table as required.
:param nmrelation: Optional reference from the referencing table to a 3rd N:M table
%End

void setFeature( const QgsFeature &feature );
void setFeature( const QgsFeature &feature, bool update = true );
%Docstring
Sets the ``feature`` being edited and updates the UI unless ``update`` is set to ``False``
%End

void setEditorContext( const QgsAttributeEditorContext &context );
%Docstring
Sets the editor ``context``
%End

QgsIFeatureSelectionManager *featureSelectionManager();
%Docstring
Expand Down Expand Up @@ -129,6 +138,21 @@ Determines if the "Save child layer edits" button should be shown
.. versionadded:: 3.14
%End

QgsFeature feature() const;
%Docstring
Returns the widget's current feature

.. versionadded:: 3.14
%End

public slots:

void parentFormValueChanged( const QString &attribute, const QVariant &newValue );
%Docstring
Called when an ``attribute`` value in the parent widget has changed to ``newValue``

.. versionadded:: 3.14
%End

};

Expand Down
7 changes: 7 additions & 0 deletions resources/function_help/json/current_parent_value
@@ -0,0 +1,7 @@
{
"name": "current_parent_value",
"type": "function",
"description": "Only usable in an embedded form context, this function returns the current, unsaved value of a field in the parent form currently being edited. This will differ from the parent feature's actual attribute values for features which are currently being edited or have not yet been added to a parent layer. When used in a value-relation widget filter expression, this function should be wrapped into a 'coalesce()' that can retrieve the actual parent feature from the layer when the form is not used in an embedded context.",
"arguments": [ {"arg":"field_name","description":"a field name in the current parent form"}],
"examples": [ { "expression":"current_parent_value( 'FIELD_NAME' )","returns":"The current value of a field 'FIELD_NAME' in the parent form."} ]
}
16 changes: 16 additions & 0 deletions src/core/expression/qgsexpression.cpp
Expand Up @@ -826,6 +826,22 @@ void QgsExpression::initVariableHelp()
sVariableHelpTexts()->insert( QStringLiteral( "current_geometry" ), QCoreApplication::translate( "current_geometry", "Represents the geometry of the feature currently being edited in the form or the table row. Can be used in a form/row context to filter the related features." ) );
sVariableHelpTexts()->insert( QStringLiteral( "current_feature" ), QCoreApplication::translate( "current_feature", "Represents the feature currently being edited in the form or the table row. Can be used in a form/row context to filter the related features." ) );

//parent form context variable
sVariableHelpTexts()->insert( QStringLiteral( "current_parent_geometry" ), QCoreApplication::translate( "current_parent_geometry",
"Only usable in an embedded form context, "
"represents the geometry of the feature currently being edited in the parent form.\n"
"Can be used in a form/row context to filter the related features using a value "
"from the feature currently edited in the parent form, to make sure that the filter "
"still works with standalone forms it is recommended to wrap this variable in a "
"'coalesce()'." ) );
sVariableHelpTexts()->insert( QStringLiteral( "current_parent_feature" ), QCoreApplication::translate( "current_parent_feature",
"Only usable in an embedded form context, "
"represents the feature currently being edited in the parent form.\n"
"Can be used in a form/row context to filter the related features using a value "
"from the feature currently edited in the parent form, to make sure that the filter "
"still works with standalone forms it is recommended to wrap this variable in a "
"'coalesce()'." ) );

//form variable
sVariableHelpTexts()->insert( QStringLiteral( "form_mode" ), QCoreApplication::translate( "form_mode", "What the form is used for, like AddFeatureMode, SingleEditMode, MultiEditMode, SearchMode, AggregateSearchMode or IdentifyMode as string." ) );
}
Expand Down
37 changes: 37 additions & 0 deletions src/core/expression/qgsexpressioncontextutils.cpp
Expand Up @@ -134,6 +134,31 @@ class GetCurrentFormFieldValue : public QgsScopedExpressionFunction

};

class GetCurrentParentFormFieldValue : public QgsScopedExpressionFunction
{
public:
GetCurrentParentFormFieldValue( )
: QgsScopedExpressionFunction( QStringLiteral( "current_parent_value" ), QgsExpressionFunction::ParameterList() << QStringLiteral( "field_name" ), QStringLiteral( "Form" ) )
{}

QVariant func( const QVariantList &values, const QgsExpressionContext *context, QgsExpression *, const QgsExpressionNodeFunction * ) override
{
QString fieldName( values.at( 0 ).toString() );
const QgsFeature feat( context->variable( QStringLiteral( "current_parent_feature" ) ).value<QgsFeature>() );
if ( fieldName.isEmpty() || ! feat.isValid( ) )
{
return QVariant();
}
return feat.attribute( fieldName ) ;
}

QgsScopedExpressionFunction *clone() const override
{
return new GetCurrentParentFormFieldValue( );
}

};


class GetProcessingParameterValue : public QgsScopedExpressionFunction
{
Expand Down Expand Up @@ -172,6 +197,17 @@ QgsExpressionContextScope *QgsExpressionContextUtils::formScope( const QgsFeatur
return scope;
}


QgsExpressionContextScope *QgsExpressionContextUtils::parentFormScope( const QgsFeature &parentFormFeature, const QString &parentFormMode )
{
QgsExpressionContextScope *scope = new QgsExpressionContextScope( QObject::tr( "Parent Form" ) );
scope->addFunction( QStringLiteral( "current_parent_value" ), new GetCurrentParentFormFieldValue( ) );
scope->setVariable( QStringLiteral( "current_parent_geometry" ), parentFormFeature.geometry( ), true );
scope->setVariable( QStringLiteral( "current_parent_feature" ), parentFormFeature, true );
scope->setVariable( QStringLiteral( "parent_form_mode" ), parentFormMode, true );
return scope;
}

QgsExpressionContextScope *QgsExpressionContextUtils::projectScope( const QgsProject *project )
{
if ( !project )
Expand Down Expand Up @@ -766,6 +802,7 @@ void QgsExpressionContextUtils::registerContextFunctions()
QgsExpression::registerFunction( new GetLayerVisibility( QList<QgsMapLayer *>(), 0.0 ) );
QgsExpression::registerFunction( new GetProcessingParameterValue( QVariantMap() ) );
QgsExpression::registerFunction( new GetCurrentFormFieldValue( ) );
QgsExpression::registerFunction( new GetCurrentParentFormFieldValue( ) );
}

bool QgsScopedExpressionFunction::usesGeometry( const QgsExpressionNodeFunction *node ) const
Expand Down
10 changes: 9 additions & 1 deletion src/core/expression/qgsexpressioncontextutils.h
Expand Up @@ -54,13 +54,21 @@ class CORE_EXPORT QgsExpressionContextUtils
static QgsExpressionContextScope *globalScope() SIP_FACTORY;

/**
* Creates a new scope which contains functions and variables from the current attribute form/table \a feature.
* Creates a new scope which contains functions and variables from the current attribute form/table \a formFeature.
* The variables and values in this scope will reflect the current state of the form/row being edited.
* The \a formMode (SingleEditMode etc.) is passed as text
* \since QGIS 3.2
*/
static QgsExpressionContextScope *formScope( const QgsFeature &formFeature = QgsFeature( ), const QString &formMode = QString() ) SIP_FACTORY;

/**
* Creates a new scope which contains functions and variables from the current parent attribute form/table \a formFeature.
* The variables and values in this scope will reflect the current state of the parent form/row being edited.
* The \a formMode (SingleEditMode etc.) is passed as text
* \since QGIS 3.14
*/
static QgsExpressionContextScope *parentFormScope( const QgsFeature &formFeature = QgsFeature( ), const QString &formMode = QString() ) SIP_FACTORY;

/**
* Sets a global context variable. This variable will be contained within scopes retrieved via
* globalScope().
Expand Down

0 comments on commit 3246a92

Please sign in to comment.