Skip to content

Commit

Permalink
Merge pull request #3418 from nyalldawson/default_values2
Browse files Browse the repository at this point in the history
[FEATURE] Client side default values
  • Loading branch information
nyalldawson committed Aug 30, 2016
2 parents 891370f + 97d7b73 commit 0658640
Show file tree
Hide file tree
Showing 38 changed files with 856 additions and 118 deletions.
43 changes: 42 additions & 1 deletion python/core/qgsfield.sip
Expand Up @@ -42,9 +42,20 @@ class QgsField
bool operator==( const QgsField& other ) const;
bool operator!=( const QgsField& other ) const;

//! Gets the name of the field
/** Returns the name of the field.
* @see setName()
* @see displayName()
*/
QString name() const;

/** Returns the name to use when displaying this field. This will be the
* field alias if set, otherwise the field name.
* @see name()
* @see alias()
* @note added in QGIS 3.0
*/
QString displayName() const;

//! Gets variant type of the field as it will be retrieved from data source
QVariant::Type type() const;

Expand Down Expand Up @@ -115,6 +126,36 @@ class QgsField
*/
void setComment( const QString& comment );

/** Returns the expression used when calculating the default value for the field.
* @returns expression evaluated when calculating default values for field, or an
* empty string if no default is set
* @note added in QGIS 3.0
* @see setDefaultValueExpression()
*/
QString defaultValueExpression() const;

/** Sets an expression to use when calculating the default value for the field.
* @param expression expression to evaluate when calculating default values for field. Pass
* an empty expression to clear the default.
* @note added in QGIS 3.0
* @see defaultValueExpression()
*/
void setDefaultValueExpression( const QString& expression );

/** Returns the alias for the field (the friendly displayed name of the field ),
* or an empty string if there is no alias.
* @see setAlias()
* @note added in QGIS 3.0
*/
QString alias() const;

/** Sets the alias for the field (the friendly displayed name of the field ).
* @param alias field alias, or empty string to remove an existing alias
* @see alias()
* @note added in QGIS 3.0
*/
void setAlias( const QString& alias );

/** Formats string for display*/
QString displayString( const QVariant& v ) const;

Expand Down
36 changes: 36 additions & 0 deletions python/core/qgsvectorlayer.sip
Expand Up @@ -1202,6 +1202,42 @@ class QgsVectorLayer : QgsMapLayer
// marked as const as these are just caches, and need to be created from const accessors
void createJoinCaches() const;

/** Returns the calculated default value for the specified field index. The default
* value may be taken from a client side default value expression (see setDefaultValueExpression())
* or taken from the underlying data provider.
* @param index field index
* @param feature optional feature to use for default value evaluation. If passed,
* then properties from the feature (such as geometry) can be used when calculating
* the default value.
* @param context optional expression context to evaluate expressions again. If not
* specified, a default context will be created
* @return calculated default value
* @note added in QGIS 3.0
* @see setDefaultValueExpression()
*/
QVariant defaultValue( int index, const QgsFeature& feature = QgsFeature(),
QgsExpressionContext* context = nullptr ) const;

/** Sets an expression to use when calculating the default value for a field.
* @param index field index
* @param expression expression to evaluate when calculating default values for field. Pass
* an empty expression to clear the default.
* @note added in QGIS 3.0
* @see defaultValue()
* @see defaultValueExpression()
*/
void setDefaultValueExpression( int index, const QString& expression );

/** Returns the expression used when calculating the default value for a field.
* @param index field index
* @returns expression evaluated when calculating default values for field, or an
* empty string if no default is set
* @note added in QGIS 3.0
* @see defaultValue()
* @see setDefaultValueExpression()
*/
QString defaultValueExpression( int index ) const;

/** Calculates a list of unique values contained within an attribute in the layer. Note that
* in some circumstances when unsaved changes are present for the layer then the returned list
* may contain outdated values (for instance when the attribute value in a saved feature has
Expand Down
7 changes: 7 additions & 0 deletions python/gui/editorwidgets/core/qgseditorconfigwidget.sip
Expand Up @@ -38,5 +38,12 @@ class QgsEditorConfigWidget : QWidget

virtual QgsEditorWidgetConfig config() = 0;
virtual void setConfig( const QgsEditorWidgetConfig& config ) = 0;

signals:

/** Emitted when the configuration of the widget is changed.
* @note added in QGIS 3.0
*/
void changed();
};

15 changes: 13 additions & 2 deletions src/app/qgisapp.cpp
Expand Up @@ -7345,6 +7345,7 @@ void QgisApp::editPaste( QgsMapLayer *destinationLayer )
remap.insert( idx, dst );
}

QgsExpressionContext context = pasteVectorLayer->createExpressionContext();
int dstAttrCount = pasteVectorLayer->fields().count();

QgsFeatureList::iterator featureIt = features.begin();
Expand All @@ -7356,8 +7357,18 @@ void QgisApp::editPaste( QgsMapLayer *destinationLayer )
// pre-initialized with default values
for ( int dst = 0; dst < dstAttr.count(); ++dst )
{
QVariant defVal( pasteVectorLayer->dataProvider()->defaultValue( dst ) );
if ( !defVal.isNull() )
QVariant defVal;
if ( !pasteVectorLayer->defaultValueExpression( dst ).isEmpty() )
{
// client side default expression set - use this in preference to provider default
defVal = pasteVectorLayer->defaultValue( dst, *featureIt, &context );
}
else
{
defVal = pasteVectorLayer->dataProvider()->defaultValue( dst );
}

if ( defVal.isValid() && !defVal.isNull() )
{
dstAttr[ dst ] = defVal;
}
Expand Down
81 changes: 72 additions & 9 deletions src/app/qgsattributetypedialog.cpp
Expand Up @@ -69,10 +69,12 @@ QgsAttributeTypeDialog::QgsAttributeTypeDialog( QgsVectorLayer *vl, int fieldIdx
isFieldEditableCheckBox->setEnabled( false );
}

connect( mExpressionWidget, SIGNAL( expressionChanged( QString ) ), this, SLOT( defaultExpressionChanged() ) );

QSettings settings;
restoreGeometry( settings.value( "/Windows/QgsAttributeTypeDialog/geometry" ).toByteArray() );

constraintExpression->setLayer( vl );
constraintExpressionWidget->setLayer( vl );
}

QgsAttributeTypeDialog::~QgsAttributeTypeDialog()
Expand Down Expand Up @@ -152,12 +154,16 @@ void QgsAttributeTypeDialog::setWidgetType( const QString& type )
stackedWidget->addWidget( cfgWdg );
stackedWidget->setCurrentWidget( cfgWdg );
mEditorConfigWidgets.insert( type, cfgWdg );
connect( cfgWdg, SIGNAL( changed() ), this, SLOT( defaultExpressionChanged() ) );
}
else
{
QgsDebugMsg( "Oops, couldn't create editor widget config dialog..." );
}
}

//update default expression preview
defaultExpressionChanged();
}

void QgsAttributeTypeDialog::setWidgetConfig( const QgsEditorWidgetConfig& config )
Expand All @@ -180,29 +186,39 @@ bool QgsAttributeTypeDialog::labelOnTop() const
return labelOnTopCheckBox->isChecked();
}

void QgsAttributeTypeDialog::setExpressionDescription( const QString &desc )
void QgsAttributeTypeDialog::setConstraintExpressionDescription( const QString &desc )
{
constraintExpressionDescription->setText( desc );
leConstraintExpressionDescription->setText( desc );
}

QString QgsAttributeTypeDialog::expressionDescription()
QString QgsAttributeTypeDialog::constraintExpressionDescription()
{
return constraintExpressionDescription->text();
return leConstraintExpressionDescription->text();
}

bool QgsAttributeTypeDialog::notNull() const
{
return notNullCheckBox->isChecked();
}

void QgsAttributeTypeDialog::setExpression( const QString &str )
void QgsAttributeTypeDialog::setConstraintExpression( const QString &str )
{
constraintExpressionWidget->setField( str );
}

QString QgsAttributeTypeDialog::defaultValueExpression() const
{
return mExpressionWidget->expression();
}

void QgsAttributeTypeDialog::setDefaultValueExpression( const QString& expression )
{
constraintExpression->setField( str );
mExpressionWidget->setExpression( expression );
}

QString QgsAttributeTypeDialog::expression() const
QString QgsAttributeTypeDialog::constraintExpression() const
{
return constraintExpression->asExpression();
return constraintExpressionWidget->asExpression();
}

void QgsAttributeTypeDialog::setFieldEditable( bool editable )
Expand All @@ -221,3 +237,50 @@ void QgsAttributeTypeDialog::on_selectionListWidget_currentRowChanged( int index

setWidgetType( editType );
}

void QgsAttributeTypeDialog::defaultExpressionChanged()
{
QString expression = mExpressionWidget->expression();
if ( expression.isEmpty() )
{
mDefaultPreviewLabel->setText( QString() );
return;
}

QgsExpressionContext context = mLayer->createExpressionContext();

if ( !mPreviewFeature.isValid() )
{
// get first feature
QgsFeatureIterator it = mLayer->getFeatures( QgsFeatureRequest().setLimit( 1 ) );
it.nextFeature( mPreviewFeature );
}

context.setFeature( mPreviewFeature );

QgsExpression exp = QgsExpression( expression );
exp.prepare( &context );

if ( exp.hasParserError() )
{
mDefaultPreviewLabel->setText( "<i>" + exp.parserErrorString() + "</i>" );
return;
}

QVariant val = exp.evaluate( &context );
if ( exp.hasEvalError() )
{
mDefaultPreviewLabel->setText( "<i>" + exp.evalErrorString() + "</i>" );
return;
}

QString previewText = val.toString();

QgsEditorWidgetFactory *factory = QgsEditorWidgetRegistry::instance()->factory( editorWidgetType() );
if ( factory )
{
previewText = factory->representValue( mLayer, mFieldIdx, editorWidgetConfig(), QVariant(), val );
}

mDefaultPreviewLabel->setText( "<i>" + previewText + "</i>" );
}
28 changes: 22 additions & 6 deletions src/app/qgsattributetypedialog.h
Expand Up @@ -20,6 +20,7 @@
#include "ui_qgsattributetypeedit.h"

#include "qgseditorconfigwidget.h"
#include "qgsfeature.h"

class QDialog;

Expand Down Expand Up @@ -78,31 +79,42 @@ class APP_EXPORT QgsAttributeTypeDialog: public QDialog, private Ui::QgsAttribut
*/
bool notNull() const;

/*
/**
* Setter for constraint expression description
* @param desc the expression description
* @note added in QGIS 2.16
**/
void setExpressionDescription( const QString &desc );
void setConstraintExpressionDescription( const QString &desc );

/*
/**
* Getter for constraint expression description
* @return the expression description
* @note added in QGIS 2.16
**/
QString expressionDescription();
QString constraintExpressionDescription();

/**
* Getter for the constraint expression
* @note added in QGIS 2.16
*/
QString expression() const;
QString constraintExpression() const;

/**
* Setter for the constraint expression
* @note added in QGIS 2.16
*/
void setExpression( const QString &str );
void setConstraintExpression( const QString &str );

/**
* Returns the expression used for the field's default value, or
* an empty string if no default value expression is set.
*/
QString defaultValueExpression() const;

/**
* Sets the expression used for the field's default value
*/
void setDefaultValueExpression( const QString& expression );

private slots:
/**
Expand All @@ -111,6 +123,8 @@ class APP_EXPORT QgsAttributeTypeDialog: public QDialog, private Ui::QgsAttribut
*/
void on_selectionListWidget_currentRowChanged( int index );

void defaultExpressionChanged();

private:
QgsVectorLayer *mLayer;
int mFieldIdx;
Expand All @@ -119,6 +133,8 @@ class APP_EXPORT QgsAttributeTypeDialog: public QDialog, private Ui::QgsAttribut

//! Cached configuration dialog (lazy loaded)
QMap< QString, QgsEditorConfigWidget* > mEditorConfigWidgets;

QgsFeature mPreviewFeature;
};

#endif
7 changes: 7 additions & 0 deletions src/app/qgsfeatureaction.cpp
Expand Up @@ -145,6 +145,8 @@ bool QgsFeatureAction::addFeature( const QgsAttributeMap& defaultAttributes, boo
bool reuseLastValues = settings.value( "/qgis/digitizing/reuseLastValues", false ).toBool();
QgsDebugMsg( QString( "reuseLastValues: %1" ).arg( reuseLastValues ) );

QgsExpressionContext context = mLayer->createExpressionContext();

// add the fields to the QgsFeature
const QgsFields& fields = mLayer->fields();
mFeature->initAttributes( fields.count() );
Expand All @@ -156,6 +158,11 @@ bool QgsFeatureAction::addFeature( const QgsAttributeMap& defaultAttributes, boo
{
v = defaultAttributes.value( idx );
}
else if ( !mLayer->defaultValueExpression( idx ).isEmpty() )
{
// client side default expression set - use this in preference to reusing last value
v = mLayer->defaultValue( idx, *mFeature, &context );
}
else if ( reuseLastValues && sLastUsedValues.contains( mLayer ) && sLastUsedValues[ mLayer ].contains( idx ) && !pkAttrList.contains( idx ) )
{
v = sLastUsedValues[ mLayer ][idx];
Expand Down

0 comments on commit 0658640

Please sign in to comment.