Skip to content
Permalink
Browse files

Fix add attribute dialog

Fix #10337
  • Loading branch information
m-kuhn committed May 25, 2014
1 parent 3871ce7 commit 81293dbe44d03c751142cb2df0c90c149f775d80
Showing with 127 additions and 56 deletions.
  1. +34 −30 src/app/qgsfeatureaction.cpp
  2. +5 −2 src/app/qgsfeatureaction.h
  3. +16 −0 src/gui/qgsattributedialog.h
  4. +48 −24 src/gui/qgsattributeform.cpp
  5. +24 −0 src/gui/qgsattributeform.h
@@ -165,10 +165,10 @@ bool QgsFeatureAction::addFeature( const QgsAttributeMap& defaultAttributes )
QgsDebugMsg( QString( "Using specified default %1 for %2" ).arg( defaultAttributes.value( idx ).toString() ).arg( idx ) );
mFeature.setAttribute( idx, defaultAttributes.value( idx ) );
}
else if ( reuseLastValues && mLastUsedValues.contains( mLayer ) && mLastUsedValues[ mLayer ].contains( idx ) )
else if ( reuseLastValues && sLastUsedValues.contains( mLayer ) && sLastUsedValues[ mLayer ].contains( idx ) )
{
QgsDebugMsg( QString( "reusing %1 for %2" ).arg( mLastUsedValues[ mLayer ][idx].toString() ).arg( idx ) );
mFeature.setAttribute( idx, mLastUsedValues[ mLayer ][idx] );
QgsDebugMsg( QString( "reusing %1 for %2" ).arg( sLastUsedValues[ mLayer ][idx].toString() ).arg( idx ) );
mFeature.setAttribute( idx, sLastUsedValues[ mLayer ][idx] );
}
else
{
@@ -178,7 +178,7 @@ bool QgsFeatureAction::addFeature( const QgsAttributeMap& defaultAttributes )

bool res = false;

mLayer->beginEditCommand( text() );


// show the dialog to enter attribute values
bool isDisabledAttributeValuesDlg = settings.value( "/qgis/digitizing/disable_enter_attribute_values_dialog", false ).toBool();
@@ -196,7 +196,13 @@ bool QgsFeatureAction::addFeature( const QgsAttributeMap& defaultAttributes )
}
if ( isDisabledAttributeValuesDlg )
{
mLayer->beginEditCommand( text() );
res = mLayer->addFeature( mFeature );

if ( res )
mLayer->endEditCommand();
else
mLayer->destroyEditCommand();
}
else
{
@@ -205,36 +211,34 @@ bool QgsFeatureAction::addFeature( const QgsAttributeMap& defaultAttributes )
origValues = mFeature.attributes();

QgsAttributeDialog *dialog = newDialog( false );
if ( dialog->exec() )
{
if ( reuseLastValues )
{
for ( int idx = 0; idx < fields.count(); ++idx )
{
const QgsAttributes &newValues = mFeature.attributes();
if ( origValues[idx] != newValues[idx] )
{
QgsDebugMsg( QString( "saving %1 for %2" ).arg( mLastUsedValues[ mLayer ][idx].toString() ).arg( idx ) );
mLastUsedValues[ mLayer ][idx] = newValues[idx];
}
}
}

res = mLayer->addFeature( mFeature );
}
else
dialog->setIsAddDialog( true );
dialog->setEditCommandMessage( text() );
dialog->exec();
if ( reuseLastValues )
{
QgsDebugMsg( "Adding feature to layer failed" );
res = false;
connect( dialog->dialog(), SIGNAL( featureSaved( const QgsFeature& feature ) ), this, SLOT( updateLastUsedValues( const QgsFeature& feature ) ) );
}
}

if ( res )
mLayer->endEditCommand();
else
mLayer->destroyEditCommand();

return res;
}

QMap<QgsVectorLayer *, QgsAttributeMap> QgsFeatureAction::mLastUsedValues;
void QgsFeatureAction::updateLastUsedValues( const QgsFeature& feature )
{
QgsAttributeForm* form = qobject_cast<QgsAttributeForm*>( sender() );
Q_ASSERT( form );

QgsFields fields = mLayer->pendingFields();
for ( int idx = 0; idx < fields.count(); ++idx )
{
const QgsAttributes &newValues = feature.attributes();
QgsAttributeMap origValues = sLastUsedValues[ mLayer ];
if ( origValues[idx] != newValues[idx] )
{
QgsDebugMsg( QString( "saving %1 for %2" ).arg( sLastUsedValues[ mLayer ][idx].toString() ).arg( idx ) );
sLastUsedValues[ mLayer ][idx] = newValues[idx];
}
}
}

QMap<QgsVectorLayer *, QgsAttributeMap> QgsFeatureAction::sLastUsedValues;
@@ -2,7 +2,7 @@
qgsfeatureaction.h - description
------------------
begin : 2010-09-20
copyright : (C) 2010 by Jürgen E. Fischer
copyright : (C) 2010 by Jürgen E. Fischer
email : jef at norbit dot de
***************************************************************************/

@@ -52,6 +52,9 @@ class APP_EXPORT QgsFeatureAction : public QAction
*/
bool addFeature( const QgsAttributeMap& defaultAttributes = QgsAttributeMap() );

private slots:
void updateLastUsedValues( const QgsFeature& feature );

private:
QgsAttributeDialog *newDialog( bool cloneFeature );

@@ -60,7 +63,7 @@ class APP_EXPORT QgsFeatureAction : public QAction
int mAction;
int mIdx;

static QMap<QgsVectorLayer *, QgsAttributeMap> mLastUsedValues;
static QMap<QgsVectorLayer *, QgsAttributeMap> sLastUsedValues;
};

#endif
@@ -93,6 +93,22 @@ class GUI_EXPORT QgsAttributeDialog : public QObject
*/
bool editable() { return mAttributeForm->editable(); }

/**
* Toggles the form mode between edit feature and add feature.
* If set to true, the dialog will be editable even with an invalid feature.
* If set to true, the dialog will add a new feature when the form is accepted.
*
* @param isAddDialog If set to true, turn this dialog into an add feature dialog.
*/
void setIsAddDialog( bool isAddDialog ) { mAttributeForm->setIsAddDialog( isAddDialog ); }

/**
* Sets the edit command message (Undo) that will be used when the dialog is accepted
*
* @param message The message
*/
void setEditCommandMessage( const QString& message ) { mAttributeForm->setEditCommandMessage( message ); }

public slots:
void accept();

@@ -42,6 +42,8 @@ QgsAttributeForm::QgsAttributeForm( QgsVectorLayer* vl, const QgsFeature feature
, mContext( context )
, mFormNr( sFormCounter++ )
, mIsSaving( false )
, mIsAddDialog( false )
, mEditCommandMessage( tr( "Attributes changed" ) )
{
init();
initPython();
@@ -60,11 +62,16 @@ QgsAttributeForm::~QgsAttributeForm()
void QgsAttributeForm::hideButtonBox()
{
mButtonBox->hide();

// Make sure that changes are taken into account if somebody tries to figure out if there have been some
connect( mLayer, SIGNAL( beforeModifiedCheck() ), this, SLOT( save() ) );
}

void QgsAttributeForm::showButtonBox()
{
mButtonBox->show();

disconnect( mLayer, SIGNAL( beforeModifiedCheck() ), this, SLOT( save() ) );
}

void QgsAttributeForm::addInterface( QgsAttributeFormInterface* iface )
@@ -77,6 +84,13 @@ bool QgsAttributeForm::editable()
return mFeature.isValid() && mLayer->isEditable() ;
}

void QgsAttributeForm::setIsAddDialog( bool isAddDialog )
{
mIsAddDialog = isAddDialog;

synchronizeEnabledState();
}

void QgsAttributeForm::changeAttribute( const QString& field, const QVariant& value )
{
Q_FOREACH( QgsWidgetWrapper* ww, mWidgets )
@@ -118,7 +132,8 @@ bool QgsAttributeForm::save()
if ( !success )
return false;

if ( mFeature.isValid() )

if ( mFeature.isValid() || mIsAddDialog )
{
bool doUpdate = false;

@@ -154,28 +169,43 @@ bool QgsAttributeForm::save()

if ( doUpdate )
{
mLayer->beginEditCommand( tr( "Attributes changed" ) );

for ( int i = 0; i < dst.count(); ++i )
{
if ( dst[i] == src[i] || !src[i].isValid() )
continue;
mLayer->beginEditCommand( mEditCommandMessage );

success &= mLayer->changeAttributeValue( mFeature.id(), i, dst[i], src[i] );
}

if ( success )
if ( mIsAddDialog )
{
mLayer->endEditCommand();
mFeature.setAttributes( dst );
mFeature.setValid( true );
mLayer->beginEditCommand( mEditCommandMessage );
bool res = mLayer->addFeature( updatedFeature );
if ( res )
mLayer->endEditCommand();
else
mLayer->destroyEditCommand();
}
else
{
mLayer->destroyEditCommand();
for ( int i = 0; i < dst.count(); ++i )
{
if ( dst[i] == src[i] || !src[i].isValid() )
continue;

success &= mLayer->changeAttributeValue( mFeature.id(), i, dst[i], src[i] );
}

if ( success )
{
mLayer->endEditCommand();
mFeature.setAttributes( dst );
}
else
{
mLayer->destroyEditCommand();
}
}
}
}

emit featureSaved( mFeature );

mIsSaving = false;

return success;
@@ -227,21 +257,16 @@ void QgsAttributeForm::onAttributeDeleted( int idx )

void QgsAttributeForm::synchronizeEnabledState()
{
bool isEditable = ( mFeature.isValid() || mIsAddDialog ) && mLayer->isEditable();

Q_FOREACH( QgsWidgetWrapper* ww, mWidgets )
{
if ( mFeature.isValid() && mLayer->isEditable() )
{
ww->setEnabled( true );
}
else
{
ww->setEnabled( false );
}
ww->setEnabled( isEditable );
}

QPushButton* okButton = mButtonBox->button( QDialogButtonBox::Ok );
if ( okButton )
okButton->setEnabled( mFeature.isValid() && mLayer->isEditable() );
okButton->setEnabled( isEditable );
}

void QgsAttributeForm::init()
@@ -360,7 +385,6 @@ void QgsAttributeForm::init()
connect( mButtonBox, SIGNAL( accepted() ), this, SLOT( accept() ) );
connect( mButtonBox, SIGNAL( rejected() ), this, SLOT( resetValues() ) );

connect( mLayer, SIGNAL( beforeModifiedCheck() ), this, SLOT( save() ) );
connect( mLayer, SIGNAL( editingStarted() ), this, SLOT( synchronizeEnabledState() ) );
connect( mLayer, SIGNAL( editingStopped() ), this, SLOT( synchronizeEnabledState() ) );

@@ -50,6 +50,22 @@ class GUI_EXPORT QgsAttributeForm : public QWidget

bool editable();

/**
* Toggles the form mode between edit feature and add feature.
* If set to true, the dialog will be editable even with an invalid feature.
* If set to true, the dialog will add a new feature when the form is accepted.
*
* @param isAddDialog If set to true, turn this dialog into an add feature dialog.
*/
void setIsAddDialog( bool isAddDialog );

/**
* Sets the edit command message (Undo) that will be used when the dialog is accepted
*
* @param message The message
*/
void setEditCommandMessage( const QString& message ) { mEditCommandMessage = message; }

signals:
/**
* Notifies about changes of attributes
@@ -68,6 +84,11 @@ class GUI_EXPORT QgsAttributeForm : public QWidget
*/
void beforeSave( bool& ok );

/**
* Is emitted, when a feature is changed or added
*/
void featureSaved( const QgsFeature& feature );

public slots:
void changeAttribute( const QString& field, const QVariant& value );
void setFeature( const QgsFeature& feature );
@@ -125,6 +146,9 @@ class GUI_EXPORT QgsAttributeForm : public QWidget

//! Set to true while saving to prevent recursive saves
bool mIsSaving;
bool mIsAddDialog;

QString mEditCommandMessage;
};

#endif // QGSATTRIBUTEFORM_H

4 comments on commit 81293db

@blazek

This comment has been minimized.

Copy link
Member

@blazek blazek replied Dec 9, 2014

Without calling setIsAddDialog( True ) on a dialog acquired in python via iface.getFeatureForm(), the dialog is disabled for new features. But as this method is new in 2.4, I have to fiddle with QGis.QGIS_VERSION_INT.

Does not this commit breaks plugins using attribute dialog for new features?

@m-kuhn

This comment has been minimized.

Copy link
Member Author

@m-kuhn m-kuhn replied Dec 9, 2014

It was actually broken before when the attribute form was changed such that an invalid feature will be used to disable the dialog whereas before an invalid feature was used to denote a new feature.
This commit made it possible to override this behavior.

I think we could call setIsAddDialog( True ) in iface.getFeatureForm for invalid features automatically to be compatible again as of 2.8.

@blazek

This comment has been minimized.

Copy link
Member

@blazek blazek replied Dec 9, 2014

I think we could call setIsAddDialog( True ) in iface.getFeatureForm for invalid features automatically to be compatible again as of 2.8.

That should work.

@m-kuhn

This comment has been minimized.

Copy link
Member Author

@m-kuhn m-kuhn replied Dec 10, 2014

Please sign in to comment.
You can’t perform that action at this time.