Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
[feature] Add per-field control over whether last entered field
values should be reused when creating new features

While there has long been a global option that causes ALL field
values for ALL layers to be remembered and reused during a QGIS
session when creating new features, this new setting offers
per-layer, per-field control over whether values should be
reused.

It allows for finer control over form behavior, where eg some
values may be desirable to reuse but others should be cleared
or set from default value expressions.
  • Loading branch information
nyalldawson committed Mar 4, 2021
1 parent 0ffd38d commit dfe734f
Show file tree
Hide file tree
Showing 11 changed files with 177 additions and 46 deletions.
19 changes: 19 additions & 0 deletions python/core/auto_generated/editform/qgseditformconfig.sip.in
Expand Up @@ -203,6 +203,25 @@ on the left hand side.
Labeling on top leaves more horizontal space for the widget itself.
%End

bool reuseLastValue( int index ) const;
%Docstring
If this returns ``True``, the widget at the given ``index`` will remember the previously
entered value from this QGIS session when creating new features.

.. seealso:: :py:func:`setReuseLastValue`

.. versionadded:: 3.20
%End

void setReuseLastValue( int index, bool reuse );
%Docstring
Sets whether the widget at the given ``index`` will remember the previously
entered value from this QGIS session when creating new features.

.. seealso:: :py:func:`reuseLastValue`

.. versionadded:: 3.20
%End


QString initFunction() const;
Expand Down
30 changes: 12 additions & 18 deletions src/app/qgsfeatureaction.cpp
Expand Up @@ -72,7 +72,7 @@ QgsAttributeDialog *QgsFeatureAction::newDialog( bool cloneFeature )

dialog->setObjectName( QStringLiteral( "featureactiondlg:%1:%2" ).arg( mLayer->id() ).arg( f->id() ) );

QList<QgsAction> actions = mLayer->actions()->actions( QStringLiteral( "Feature" ) );
const QList<QgsAction> actions = mLayer->actions()->actions( QStringLiteral( "Feature" ) );
if ( !actions.isEmpty() )
{
dialog->setContextMenuPolicy( Qt::ActionsContextMenu );
Expand All @@ -81,8 +81,7 @@ QgsAttributeDialog *QgsFeatureAction::newDialog( bool cloneFeature )
a->setEnabled( false );
dialog->addAction( a );

const auto constActions = actions;
for ( const QgsAction &action : constActions )
for ( const QgsAction &action : actions )
{
if ( !action.runable() )
continue;
Expand Down Expand Up @@ -175,8 +174,8 @@ bool QgsFeatureAction::addFeature( const QgsAttributeMap &defaultAttributes, boo
return false;

QgsSettings settings;
bool reuseLastValues = settings.value( QStringLiteral( "qgis/digitizing/reuseLastValues" ), false ).toBool();
QgsDebugMsg( QStringLiteral( "reuseLastValues: %1" ).arg( reuseLastValues ) );
const bool reuseAllLastValues = settings.value( QStringLiteral( "qgis/digitizing/reuseLastValues" ), false ).toBool();
QgsDebugMsgLevel( QStringLiteral( "reuseAllLastValues: %1" ).arg( reuseAllLastValues ), 2 );

QgsFields fields = mLayer->fields();
QgsAttributeMap initialAttributeValues;
Expand All @@ -187,7 +186,7 @@ bool QgsFeatureAction::addFeature( const QgsAttributeMap &defaultAttributes, boo
{
initialAttributeValues.insert( idx, defaultAttributes.value( idx ) );
}
else if ( reuseLastValues && sLastUsedValues()->contains( mLayer ) && ( *sLastUsedValues() )[ mLayer ].contains( idx ) )
else if ( ( reuseAllLastValues || mLayer->editFormConfig().reuseLastValue( idx ) ) && sLastUsedValues()->contains( mLayer ) && ( *sLastUsedValues() )[ mLayer ].contains( idx ) )
{
initialAttributeValues.insert( idx, ( *sLastUsedValues() )[ mLayer ][idx] );
}
Expand Down Expand Up @@ -299,21 +298,16 @@ void QgsFeatureAction::onFeatureSaved( const QgsFeature &feature )
mFeatureSaved = true;

QgsSettings settings;
bool reuseLastValues = settings.value( QStringLiteral( "qgis/digitizing/reuseLastValues" ), false ).toBool();
QgsDebugMsg( QStringLiteral( "reuseLastValues: %1" ).arg( reuseLastValues ) );

if ( reuseLastValues )
QgsFields fields = mLayer->fields();
for ( int idx = 0; idx < fields.count(); ++idx )
{
QgsFields fields = mLayer->fields();
for ( int idx = 0; idx < fields.count(); ++idx )
QgsAttributes newValues = feature.attributes();
QgsAttributeMap origValues = ( *sLastUsedValues() )[ mLayer ];
if ( origValues[idx] != newValues.at( idx ) )
{
QgsAttributes newValues = feature.attributes();
QgsAttributeMap origValues = ( *sLastUsedValues() )[ mLayer ];
if ( origValues[idx] != newValues.at( idx ) )
{
QgsDebugMsg( QStringLiteral( "saving %1 for %2" ).arg( ( *sLastUsedValues() )[ mLayer ][idx].toString() ).arg( idx ) );
( *sLastUsedValues() )[ mLayer ][idx] = newValues.at( idx );
}
QgsDebugMsg( QStringLiteral( "saving %1 for %2" ).arg( ( *sLastUsedValues() )[ mLayer ][idx].toString() ).arg( idx ) );
( *sLastUsedValues() )[ mLayer ][idx] = newValues.at( idx );
}
}
}
Expand Down
35 changes: 35 additions & 0 deletions src/core/editform/qgseditformconfig.cpp
Expand Up @@ -270,6 +270,23 @@ void QgsEditFormConfig::setLabelOnTop( int idx, bool onTop )
}
}

bool QgsEditFormConfig::reuseLastValue( int index ) const
{
if ( index >= 0 && index < d->mFields.count() )
return d->mReuseLastValue.value( d->mFields.at( index ).name(), false );
else
return false;
}

void QgsEditFormConfig::setReuseLastValue( int index, bool reuse )
{
if ( index >= 0 && index < d->mFields.count() )
{
d.detach();
d->mReuseLastValue[ d->mFields.at( index ).name()] = reuse;
}
}

QString QgsEditFormConfig::initFunction() const
{
return d->mInitFunction;
Expand Down Expand Up @@ -441,6 +458,14 @@ void QgsEditFormConfig::readXml( const QDomNode &node, QgsReadWriteContext &cont
d->mLabelOnTop.insert( labelOnTopElement.attribute( QStringLiteral( "name" ) ), static_cast< bool >( labelOnTopElement.attribute( QStringLiteral( "labelOnTop" ) ).toInt() ) );
}

d->mReuseLastValue.clear();
const QDomNodeList reuseLastValueNodeList = node.namedItem( QStringLiteral( "reuseLastValue" ) ).toElement().childNodes();
for ( int i = 0; i < reuseLastValueNodeList.size(); ++i )
{
const QDomElement reuseLastValueElement = reuseLastValueNodeList.at( i ).toElement();
d->mReuseLastValue.insert( reuseLastValueElement.attribute( QStringLiteral( "name" ) ), static_cast< bool >( reuseLastValueElement.attribute( QStringLiteral( "reuseLastValue" ) ).toInt() ) );
}

// Read data defined field properties
QDomNodeList fieldDDPropertiesNodeList = node.namedItem( QStringLiteral( "dataDefinedFieldProperties" ) ).toElement().childNodes();
for ( int i = 0; i < fieldDDPropertiesNodeList.size(); ++i )
Expand Down Expand Up @@ -602,6 +627,16 @@ void QgsEditFormConfig::writeXml( QDomNode &node, const QgsReadWriteContext &con
}
node.appendChild( labelOnTopElem );

QDomElement reuseLastValueElem = doc.createElement( QStringLiteral( "reuseLastValue" ) );
for ( auto reuseLastValueIt = d->mReuseLastValue.constBegin(); reuseLastValueIt != d->mReuseLastValue.constEnd(); ++reuseLastValueIt )
{
QDomElement fieldElem = doc.createElement( QStringLiteral( "field" ) );
fieldElem.setAttribute( QStringLiteral( "name" ), reuseLastValueIt.key() );
fieldElem.setAttribute( QStringLiteral( "reuseLastValue" ), reuseLastValueIt.value() ? QStringLiteral( "1" ) : QStringLiteral( "0" ) );
reuseLastValueElem.appendChild( fieldElem );
}
node.appendChild( reuseLastValueElem );

// Store data defined field properties
QDomElement ddFieldPropsElement = doc.createElement( QStringLiteral( "dataDefinedFieldProperties" ) );
for ( auto it = d->mDataDefinedFieldProperties.constBegin(); it != d->mDataDefinedFieldProperties.constEnd(); ++it )
Expand Down
17 changes: 17 additions & 0 deletions src/core/editform/qgseditformconfig.h
Expand Up @@ -236,6 +236,23 @@ class CORE_EXPORT QgsEditFormConfig
*/
void setLabelOnTop( int idx, bool onTop );

/**
* If this returns TRUE, the widget at the given \a index will remember the previously
* entered value from this QGIS session when creating new features.
*
* \see setReuseLastValue()
* \since QGIS 3.20
*/
bool reuseLastValue( int index ) const;

/**
* Sets whether the widget at the given \a index will remember the previously
* entered value from this QGIS session when creating new features.
*
* \see reuseLastValue()
* \since QGIS 3.20
*/
void setReuseLastValue( int index, bool reuse );

// Python form init function stuff

Expand Down
2 changes: 2 additions & 0 deletions src/core/editform/qgseditformconfig_p.h
Expand Up @@ -36,6 +36,7 @@ class QgsEditFormConfigPrivate : public QSharedData
, mConfiguredRootContainer( o.mConfiguredRootContainer )
, mFieldEditables( o.mFieldEditables )
, mLabelOnTop( o.mLabelOnTop )
, mReuseLastValue( o.mReuseLastValue )
, mDataDefinedFieldProperties( o.mDataDefinedFieldProperties )
, mWidgetConfigs( o.mWidgetConfigs )
, mEditorLayout( o.mEditorLayout )
Expand Down Expand Up @@ -75,6 +76,7 @@ class QgsEditFormConfigPrivate : public QSharedData

QMap< QString, bool> mFieldEditables;
QMap< QString, bool> mLabelOnTop;
QMap< QString, bool> mReuseLastValue;
QMap< QString, QgsPropertyCollection> mDataDefinedFieldProperties;

QMap<QString, QVariantMap > mWidgetConfigs;
Expand Down
10 changes: 10 additions & 0 deletions src/gui/attributeformconfig/qgsattributetypedialog.cpp
Expand Up @@ -218,6 +218,16 @@ bool QgsAttributeTypeDialog::labelOnTop() const
return labelOnTopCheckBox->isChecked();
}

void QgsAttributeTypeDialog::setReuseLastValues( bool reuse )
{
reuseLastValuesCheckBox->setChecked( reuse );
}

bool QgsAttributeTypeDialog::reuseLastValues() const
{
return reuseLastValuesCheckBox->isChecked();
}

void QgsAttributeTypeDialog::setConstraintExpressionDescription( const QString &desc )
{
leConstraintExpressionDescription->setText( desc );
Expand Down
16 changes: 16 additions & 0 deletions src/gui/attributeformconfig/qgsattributetypedialog.h
Expand Up @@ -66,6 +66,22 @@ class GUI_EXPORT QgsAttributeTypeDialog: public QWidget, private Ui::QgsAttribut
*/
bool labelOnTop() const;

/**
* Setter for checkbox to reuse last entered values for the field.
*
* \see reuseLastValues()
* \since QGIS 3.20
*/
void setReuseLastValues( bool reuse );

/**
* Getter for checkbox to reuse last entered values for the field.
*
* \see setReuseLastValues()
* \since QGIS 3.20
*/
bool reuseLastValues() const;

/**
* Setter for label alias
*/
Expand Down
4 changes: 4 additions & 0 deletions src/gui/vector/qgsattributesformproperties.cpp
Expand Up @@ -253,6 +253,7 @@ void QgsAttributesFormProperties::loadAttributeTypeDialog()
mAttributeTypeDialog->setComment( cfg.mComment );
mAttributeTypeDialog->setFieldEditable( cfg.mEditable );
mAttributeTypeDialog->setLabelOnTop( cfg.mLabelOnTop );
mAttributeTypeDialog->setReuseLastValues( cfg.mReuseLastValues );
mAttributeTypeDialog->setNotNull( constraints.constraints() & QgsFieldConstraints::ConstraintNotNull );
mAttributeTypeDialog->setNotNullEnforced( constraints.constraintStrength( QgsFieldConstraints::ConstraintNotNull ) == QgsFieldConstraints::ConstraintStrengthHard );
mAttributeTypeDialog->setUnique( constraints.constraints() & QgsFieldConstraints::ConstraintUnique );
Expand Down Expand Up @@ -296,6 +297,7 @@ void QgsAttributesFormProperties::storeAttributeTypeDialog()
cfg.mComment = mLayer->fields().at( mAttributeTypeDialog->fieldIdx() ).comment();
cfg.mEditable = mAttributeTypeDialog->fieldEditable();
cfg.mLabelOnTop = mAttributeTypeDialog->labelOnTop();
cfg.mReuseLastValues = mAttributeTypeDialog->reuseLastValues();
cfg.mAlias = mAttributeTypeDialog->alias();
cfg.mDataDefinedProperties = mAttributeTypeDialog->dataDefinedProperties();

Expand Down Expand Up @@ -812,6 +814,7 @@ void QgsAttributesFormProperties::apply()

editFormConfig.setReadOnly( idx, !cfg.mEditable );
editFormConfig.setLabelOnTop( idx, cfg.mLabelOnTop );
editFormConfig.setReuseLastValue( idx, cfg.mReuseLastValues );

if ( cfg.mDataDefinedProperties.count() > 0 )
{
Expand Down Expand Up @@ -913,6 +916,7 @@ QgsAttributesFormProperties::FieldConfig::FieldConfig( QgsVectorLayer *layer, in
mEditableEnabled = layer->fields().fieldOrigin( idx ) != QgsFields::OriginJoin
&& layer->fields().fieldOrigin( idx ) != QgsFields::OriginExpression;
mLabelOnTop = layer->editFormConfig().labelOnTop( idx );
mReuseLastValues = layer->editFormConfig().reuseLastValue( idx );
mFieldConstraints = layer->fields().at( idx ).constraints();
const QgsEditorWidgetSetup setup = QgsGui::editorWidgetRegistry()->findBest( layer, layer->fields().field( idx ).name() );
mEditorWidgetType = setup.type();
Expand Down
7 changes: 4 additions & 3 deletions src/gui/vector/qgsattributesformproperties.h
Expand Up @@ -170,9 +170,10 @@ class GUI_EXPORT QgsAttributesFormProperties : public QWidget, public QgsExpress
FieldConfig() = default;
FieldConfig( QgsVectorLayer *layer, int idx );

bool mEditable = true ;
bool mEditableEnabled = true ;
bool mLabelOnTop = false ;
bool mEditable = true;
bool mEditableEnabled = true;
bool mLabelOnTop = false;
bool mReuseLastValues = false;
QgsFieldConstraints mFieldConstraints;
QPushButton *mButton = nullptr;
QString mEditorWidgetType;
Expand Down
60 changes: 35 additions & 25 deletions src/ui/attributeformconfig/qgsattributetypeedit.ui
Expand Up @@ -19,21 +19,34 @@
<property name="title">
<string>General</string>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="1" column="2">
<widget class="QLabel" name="laComment">
<layout class="QGridLayout" name="gridLayout" rowstretch="0,0,0" columnstretch="0,0,1,0,0">
<item row="2" column="1">
<widget class="QCheckBox" name="reuseLastValuesCheckBox">
<property name="toolTip">
<string>If checked, then the most recent value entered for this field will be remembered and reused when creating a new feature.</string>
</property>
<property name="text">
<string/>
<string>Reuse last entered value</string>
</property>
<property name="wordWrap">
<bool>true</bool>
<property name="checked">
<bool>false</bool>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QLabel" name="label_9">
<item row="1" column="0">
<widget class="QLabel" name="label_6">
<property name="text">
<string>Alias</string>
<string>Comment</string>
</property>
</widget>
</item>
<item row="2" column="2">
<widget class="QCheckBox" name="labelOnTopCheckBox">
<property name="text">
<string>Label on top</string>
</property>
<property name="checked">
<bool>false</bool>
</property>
</widget>
</item>
Expand All @@ -47,30 +60,30 @@
</property>
</widget>
</item>
<item row="2" column="2">
<widget class="QCheckBox" name="labelOnTopCheckBox">
<item row="0" column="0">
<widget class="QLabel" name="label_9">
<property name="text">
<string>Label on top</string>
</property>
<property name="checked">
<bool>false</bool>
<string>Alias</string>
</property>
</widget>
</item>
<item row="0" column="2">
<widget class="QLineEdit" name="mAlias"/>
</item>
<item row="0" column="3">
<item row="0" column="4">
<widget class="QgsPropertyOverrideButton" name="mAliasExpressionButton">
<property name="text">
<string>...</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_6">
<item row="0" column="1" colspan="3">
<widget class="QLineEdit" name="mAlias"/>
</item>
<item row="1" column="1" colspan="3">
<widget class="QLabel" name="laComment">
<property name="text">
<string>Comment</string>
<string/>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
Expand Down Expand Up @@ -300,7 +313,6 @@
</customwidgets>
<tabstops>
<tabstop>isFieldEditableCheckBox</tabstop>
<tabstop>labelOnTopCheckBox</tabstop>
<tabstop>mWidgetTypeComboBox</tabstop>
<tabstop>notNullCheckBox</tabstop>
<tabstop>mCheckBoxEnforceNotNull</tabstop>
Expand All @@ -313,8 +325,6 @@
</tabstops>
<resources>
<include location="../../../images/images.qrc"/>
<include location="../../../images/images.qrc"/>
<include location="../../../images/images.qrc"/>
</resources>
<connections/>
</ui>

0 comments on commit dfe734f

Please sign in to comment.