Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
attribute dialog for fixing invalid features on paste from clipboard
with the option to cancel all or store invalid anyway
  • Loading branch information
signedav committed Jan 7, 2020
1 parent a2e5f24 commit 104598b
Show file tree
Hide file tree
Showing 4 changed files with 67 additions and 30 deletions.
2 changes: 1 addition & 1 deletion python/gui/auto_generated/qgsattributedialog.sip.in
Expand Up @@ -18,7 +18,7 @@ class QgsAttributeDialog : QDialog
%End
public:

QgsAttributeDialog( QgsVectorLayer *vl, QgsFeature *thepFeature, bool featureOwner, QWidget *parent /TransferThis/ = 0, bool showDialogButtons = true, const QgsAttributeEditorContext &context = QgsAttributeEditorContext() );
QgsAttributeDialog( QgsVectorLayer *vl, QgsFeature *thepFeature, bool featureOwner, QWidget *parent /TransferThis/ = 0, bool showDialogButtons = true, const QgsAttributeEditorContext &context = QgsAttributeEditorContext(), bool showFixFeatureDialogButtons = false );
%Docstring
Create an attribute dialog for a given layer and feature

Expand Down
71 changes: 47 additions & 24 deletions src/app/qgisapp.cpp
Expand Up @@ -9590,41 +9590,64 @@ void QgisApp::pasteFromClipboard( QgsMapLayer *destinationLayer )
// values and field constraints
QgsFeatureList newFeatures {QgsVectorLayerUtils::createFeatures( pasteVectorLayer, newFeaturesDataList, &context )};

// check against hard constraints
for ( QgsFeature &f : newFeatures )
// check constraints
QgsFeatureList validFeatures = newFeatures;
QgsFeatureList invalidFeatures;
newFeatures.clear();

for ( const QgsFeature &f : qgis::as_const( validFeatures ) )
{
bool valid = true;
for ( int idx = 0; idx < pasteVectorLayer->fields().count(); ++idx )
{
QStringList errors;
valid = QgsVectorLayerUtils::validateAttribute( pasteVectorLayer, f, idx, errors, QgsFieldConstraints::ConstraintStrengthHard, QgsFieldConstraints::ConstraintOriginNotSet );
if ( !valid )
if ( !QgsVectorLayerUtils::validateAttribute( pasteVectorLayer, f, idx, errors, QgsFieldConstraints::ConstraintStrengthHard, QgsFieldConstraints::ConstraintOriginNotSet ) )
{
invalidFeatures << f;
validFeatures.removeOne( f );
break;
}
}
}

//open attribute form including option "cancel copy all" / "cancel copy of invalid" / "store invalid features"
if ( !valid )
{
//QgsFeatureAction action( tr( "Fix feature" ), f, pasteVectorLayer, QString(), -1, QgisApp::instance() );
//action.editFeature( true );
QgsAttributeDialog *dialog = new QgsAttributeDialog( pasteVectorLayer, &f, false );

if ( !f.isValid() )
dialog->setMode( QgsAttributeEditorContext::AddFeatureMode );
for ( const QgsFeature &f : qgis::as_const( invalidFeatures ) )
{
// open attribute form to fix invalid features and offer the options:
// - fix part of invalid features and save (without the unfixed) -> on cancel invalid or all fixed
// - fix part of invalid features and save (with the unfixed as invalid) -> on store invalid
// - cancel everything
QgsFeature feature = f;
QgsAttributeDialog *dialog = new QgsAttributeDialog( pasteVectorLayer, &feature, false, nullptr, true, QgsAttributeEditorContext(), true );

int feedback = dialog->exec();
int feedback = dialog->exec();

if ( feedback > 0 )
{
f.setAttributes( dialog->feature()->attributes() );
}
else
{
visibleMessageBar()->pushMessage( tr( "Paste features" ), tr( "Do not copy feature" ), Qgis::Warning, messageTimeout() );
newFeatures.removeOne( f );
}
if ( feedback == 0 )
{
//feature unfixed
continue;
}
else if ( feedback == 1 )
{
//feature fixed
feature.setAttributes( dialog->feature()->attributes() );
validFeatures << feature;
invalidFeatures.removeOne( f );
}
else if ( feedback == 10 )
{
//cancel all
break;
}
else if ( feedback == 11 )
{
//cancel all invalid
newFeatures << validFeatures;
break;
}
else if ( feedback == 12 )
{
//store all invalid
newFeatures << validFeatures << invalidFeatures;
break;
}
}

Expand Down
20 changes: 17 additions & 3 deletions src/gui/qgsattributedialog.cpp
Expand Up @@ -21,12 +21,13 @@
#include "qgshighlight.h"
#include "qgsapplication.h"
#include "qgssettings.h"
#include <QtWidgets/QPushButton>

QgsAttributeDialog::QgsAttributeDialog( QgsVectorLayer *vl, QgsFeature *thepFeature, bool featureOwner, QWidget *parent, bool showDialogButtons, const QgsAttributeEditorContext &context )
QgsAttributeDialog::QgsAttributeDialog( QgsVectorLayer *vl, QgsFeature *thepFeature, bool featureOwner, QWidget *parent, bool showDialogButtons, const QgsAttributeEditorContext &context, bool showFixFeatureDialogButtons )
: QDialog( parent )
, mOwnedFeature( featureOwner ? thepFeature : nullptr )
{
init( vl, thepFeature, context, showDialogButtons );
init( vl, thepFeature, context, showDialogButtons, showFixFeatureDialogButtons );
}

QgsAttributeDialog::~QgsAttributeDialog()
Expand Down Expand Up @@ -85,7 +86,7 @@ void QgsAttributeDialog::reject()
QDialog::reject();
}

void QgsAttributeDialog::init( QgsVectorLayer *layer, QgsFeature *feature, const QgsAttributeEditorContext &context, bool showDialogButtons )
void QgsAttributeDialog::init( QgsVectorLayer *layer, QgsFeature *feature, const QgsAttributeEditorContext &context, bool showDialogButtons, bool showFixFeatureDialogButtons )
{
QgsAttributeEditorContext trackedContext = context;
setWindowTitle( tr( "%1 - Feature Attributes" ).arg( layer->name() ) );
Expand All @@ -100,6 +101,19 @@ void QgsAttributeDialog::init( QgsVectorLayer *layer, QgsFeature *feature, const
mAttributeForm->disconnectButtonBox();
layout()->addWidget( mAttributeForm );
QDialogButtonBox *buttonBox = mAttributeForm->findChild<QDialogButtonBox *>();

if ( showFixFeatureDialogButtons )
{
QPushButton *cancelAllBtn = new QPushButton( tr( "Cancel all" ) );
QPushButton *cancelAllInvalidBtn = new QPushButton( tr( "Cancel all invalid" ) );
QPushButton *storeAllInvalidBtn = new QPushButton( tr( "Store all (even invalid)" ) );
buttonBox->addButton( cancelAllBtn, QDialogButtonBox::RejectRole );
buttonBox->addButton( cancelAllInvalidBtn, QDialogButtonBox::RejectRole );
buttonBox->addButton( storeAllInvalidBtn, QDialogButtonBox::RejectRole );
connect( cancelAllBtn, &QAbstractButton::clicked, this, [ = ]() { done( 10 ); } );
connect( cancelAllInvalidBtn, &QAbstractButton::clicked, this, [ = ]() { done( 11 ); } );
connect( storeAllInvalidBtn, &QAbstractButton::clicked, this, [ = ]() { done( 12 ); } );
}
connect( buttonBox, &QDialogButtonBox::rejected, this, &QgsAttributeDialog::reject );
connect( buttonBox, &QDialogButtonBox::accepted, this, &QgsAttributeDialog::accept );
connect( layer, &QObject::destroyed, this, &QWidget::close );
Expand Down
4 changes: 2 additions & 2 deletions src/gui/qgsattributedialog.h
Expand Up @@ -51,7 +51,7 @@ class GUI_EXPORT QgsAttributeDialog : public QDialog
* \param context The context in which this dialog is created
*
*/
QgsAttributeDialog( QgsVectorLayer *vl, QgsFeature *thepFeature, bool featureOwner, QWidget *parent SIP_TRANSFERTHIS = nullptr, bool showDialogButtons = true, const QgsAttributeEditorContext &context = QgsAttributeEditorContext() );
QgsAttributeDialog( QgsVectorLayer *vl, QgsFeature *thepFeature, bool featureOwner, QWidget *parent SIP_TRANSFERTHIS = nullptr, bool showDialogButtons = true, const QgsAttributeEditorContext &context = QgsAttributeEditorContext(), bool showFixFeatureDialogButtons = false );

~QgsAttributeDialog() override;

Expand Down Expand Up @@ -103,7 +103,7 @@ class GUI_EXPORT QgsAttributeDialog : public QDialog
void show();

private:
void init( QgsVectorLayer *layer, QgsFeature *feature, const QgsAttributeEditorContext &context, bool showDialogButtons );
void init( QgsVectorLayer *layer, QgsFeature *feature, const QgsAttributeEditorContext &context, bool showDialogButtons, bool showFixFeatureDialogButtons );

QString mSettingsPath;
// Used to sync multiple widgets for the same field
Expand Down

0 comments on commit 104598b

Please sign in to comment.