Skip to content
Permalink
Browse files
Relation editor widget support for multiple features
  • Loading branch information
domi4484 committed Nov 8, 2021
1 parent 3499d9a commit 90d3062fd369442ed56bf32a05b7375220f349dc
@@ -219,6 +219,13 @@ Duplicates a feature
void duplicateFeatures( const QgsFeatureIds &fids );
%Docstring
Duplicates features
%End

bool multiEditModeActive() const;
%Docstring
Return true if editing multiple features at a time

.. versionadded:: 3.22
%End

protected:
@@ -221,7 +221,7 @@ void QgsAbstractRelationEditorWidget::addFeature( const QgsGeometry &geometry )

if ( mNmRelation.isValid() )
{
if ( mFeatureList.size() > 1 )
if ( multiEditModeActive() )
{
QgsLogger::warning( tr( "Adding of feature not supported in multiple edit mode for n:m relations" ) );
return;
@@ -259,20 +259,25 @@ void QgsAbstractRelationEditorWidget::addFeature( const QgsGeometry &geometry )
}
else
{
int featureAdded = 0;
const auto constFieldPairs = mRelation.fieldPairs();
for ( const QgsRelation::FieldPair &fieldPair : constFieldPairs )
keyAttrs.insert( fields.indexFromName( fieldPair.referencingField() ), mFeatureList.first().attribute( fieldPair.referencedField() ) );

QgsFeature linkFeature;
if ( !vlTools->addFeature( mRelation.referencingLayer(), keyAttrs, geometry, &linkFeature ) )
return;

for ( const QgsFeature &feature : mFeatureList )
{
const auto constFieldPairs = mRelation.fieldPairs();
// First feature already added
if ( mFeatureList.first() == feature )
continue;

for ( const QgsRelation::FieldPair &fieldPair : constFieldPairs )
{
keyAttrs.insert( fields.indexFromName( fieldPair.referencingField() ), feature.attribute( fieldPair.referencedField() ) );
}
linkFeature.setAttribute( fields.indexFromName( fieldPair.referencingField() ), feature.attribute( fieldPair.referencedField() ) );

if ( vlTools->addFeature( mRelation.referencingLayer(), keyAttrs, geometry ) )
featureAdded++;
mRelation.referencingLayer()->addFeature( linkFeature );
}
if ( featureAdded == 0 )
return;
}

updateUi();
@@ -290,6 +295,12 @@ void QgsAbstractRelationEditorWidget::deleteFeatures( const QgsFeatureIds &fids
QgsVectorLayer *layer;
if ( mNmRelation.isValid() )
{
if ( multiEditModeActive() )
{
QgsLogger::warning( tr( "Deleting of features not supported in multiple edit mode for n:m relations" ) );
return;
}

// only normal relations support m:n relation
Q_ASSERT( mNmRelation.type() == QgsRelation::Normal );

@@ -402,7 +413,7 @@ void QgsAbstractRelationEditorWidget::deleteFeatures( const QgsFeatureIds &fids

void QgsAbstractRelationEditorWidget::linkFeature()
{
if ( mFeatureList.size() > 1 )
if ( multiEditModeActive() )
{
QgsLogger::warning( tr( "Linking of feature not supported in multiple edit mode" ) );
return;
@@ -436,7 +447,7 @@ void QgsAbstractRelationEditorWidget::onLinkFeatureDlgAccepted()
{
QgsFeatureSelectionDlg *selectionDlg = qobject_cast<QgsFeatureSelectionDlg *>( sender() );

if ( mFeatureList.size() > 1 )
if ( multiEditModeActive() )
{
QgsLogger::warning( tr( "Linking of feature not supported in multiple edit mode" ) );
return;
@@ -545,14 +556,14 @@ void QgsAbstractRelationEditorWidget::unlinkFeature( const QgsFeatureId fid )

void QgsAbstractRelationEditorWidget::unlinkFeatures( const QgsFeatureIds &fids )
{
if ( mFeatureList.size() > 1 )
{
QgsLogger::warning( tr( "Unlinking of features not supported in multiple edit mode" ) );
return;
}

if ( mNmRelation.isValid() )
{
if ( multiEditModeActive() )
{
QgsLogger::warning( tr( "Unlinking of features not supported in multiple edit mode for n:m relations" ) );
return;
}

// only normal relations support m:n relation
Q_ASSERT( mNmRelation.type() == QgsRelation::Normal );

@@ -675,6 +686,11 @@ void QgsAbstractRelationEditorWidget::duplicateFeatures( const QgsFeatureIds &fi
}
}

bool QgsAbstractRelationEditorWidget::multiEditModeActive() const
{
return mFeatureList.size() > 1;
}

void QgsAbstractRelationEditorWidget::showEvent( QShowEvent * )
{
updateUi();
@@ -231,6 +231,12 @@ class GUI_EXPORT QgsAbstractRelationEditorWidget : public QWidget
*/
void duplicateFeatures( const QgsFeatureIds &fids );

/**
* Return true if editing multiple features at a time
* \since QGIS 3.22
*/
bool multiEditModeActive() const;

protected:

QgsAttributeEditorContext mEditorContext;
@@ -206,13 +206,12 @@ QgsRelationEditorWidget::QgsRelationEditorWidget( const QVariantMap &config, QWi
// add multi feature editing page
mMultiEditStackedWidgetPage = new QWidget( this );
{
QVBoxLayout *vBoxLayout = new QVBoxLayout( this );
QVBoxLayout *vBoxLayout = new QVBoxLayout();
vBoxLayout->setContentsMargins( 0, 0, 0, 0 );

mMultiEditInformationLabel = new QLabel( this );
vBoxLayout->addWidget( mMultiEditInformationLabel );

mMultiEditTreeWidget = new QTreeWidget( this );
mMultiEditTreeWidget->setHeaderHidden( true );
mMultiEditTreeWidget->setSelectionMode( QTreeWidget::ExtendedSelection );
vBoxLayout->addWidget( mMultiEditTreeWidget );

mMultiEditStackedWidgetPage->setLayout( vBoxLayout );
@@ -239,14 +238,15 @@ QgsRelationEditorWidget::QgsRelationEditorWidget( const QVariantMap &config, QWi
connect( mLinkFeatureButton, &QAbstractButton::clicked, this, &QgsRelationEditorWidget::linkFeature );
connect( mUnlinkFeatureButton, &QAbstractButton::clicked, this, &QgsRelationEditorWidget::unlinkSelectedFeatures );
connect( mZoomToFeatureButton, &QAbstractButton::clicked, this, &QgsRelationEditorWidget::zoomToSelectedFeatures );
connect( mMultiEditTreeWidget, &QTreeWidget::itemSelectionChanged, this, &QgsRelationEditorWidget::updateButtons );

// Set initial state for add/remove etc. buttons
updateButtons();
}

void QgsRelationEditorWidget::initDualView( QgsVectorLayer *layer, const QgsFeatureRequest &request )
{
if ( mFeatureList.size() > 1 )
if ( multiEditModeActive() )
{
QgsLogger::warning( tr( "Dual view should not be used in multiple edit mode" ) );
return;
@@ -311,9 +311,9 @@ void QgsRelationEditorWidget::updateButtons()
bool canAddGeometry = false;
bool canRemove = false;
bool canEdit = false;
bool canLinkUnlink = false;
bool canLink = false;
bool canUnlink = false;
bool spatial = false;
const bool selectionNotEmpty = mFeatureSelectionMgr ? mFeatureSelectionMgr->selectedFeatureCount() : false;

if ( mRelation.isValid() )
{
@@ -322,7 +322,8 @@ void QgsRelationEditorWidget::updateButtons()
canAddGeometry = mRelation.referencingLayer()->isEditable();
canRemove = mRelation.referencingLayer()->isEditable();
canEdit = mRelation.referencingLayer()->isEditable();
canLinkUnlink = mRelation.referencingLayer()->isEditable();
canLink = mRelation.referencingLayer()->isEditable();
canUnlink = mRelation.referencingLayer()->isEditable();
spatial = mRelation.referencingLayer()->isSpatial();
}

@@ -336,24 +337,32 @@ void QgsRelationEditorWidget::updateButtons()
spatial = mNmRelation.referencedLayer()->isSpatial();
}

if ( mFeatureList.size() > 1 )
const bool selectionNotEmpty = mFeatureSelectionMgr ? mFeatureSelectionMgr->selectedFeatureCount() : false;
if ( multiEditModeActive() )
{
const bool multieditLinkedChildSelected = ! selectedChildFeatureIds().isEmpty();

canAddGeometry = false;
canRemove = false;
canEdit = false;
canLinkUnlink = false;
canRemove = canRemove && multieditLinkedChildSelected;
canLink = false;
canUnlink = canUnlink && multieditLinkedChildSelected;
}
else
{
canRemove = canRemove && selectionNotEmpty;
canUnlink = canUnlink && selectionNotEmpty;
}

mToggleEditingButton->setEnabled( toggleEditingButtonEnabled );
mAddFeatureButton->setEnabled( canAdd );
mAddFeatureGeometryButton->setEnabled( canAddGeometry );
mDuplicateFeatureButton->setEnabled( canEdit && selectionNotEmpty );
mLinkFeatureButton->setEnabled( canLinkUnlink );
mDeleteFeatureButton->setEnabled( canRemove && selectionNotEmpty );
mUnlinkFeatureButton->setEnabled( canLinkUnlink && selectionNotEmpty );
mLinkFeatureButton->setEnabled( canLink );
mDeleteFeatureButton->setEnabled( canRemove );
mUnlinkFeatureButton->setEnabled( canUnlink );
mZoomToFeatureButton->setEnabled( selectionNotEmpty );
mToggleEditingButton->setChecked( canEdit );
mSaveEditsButton->setEnabled( canEdit || canLinkUnlink );
mSaveEditsButton->setEnabled( canEdit || canLink || canUnlink );

mToggleEditingButton->setVisible( !mLayerInSameTransactionGroup );

@@ -369,7 +378,7 @@ void QgsRelationEditorWidget::updateButtons()

void QgsRelationEditorWidget::addFeatureGeometry()
{
if ( mFeatureList.size() > 1 )
if ( multiEditModeActive() )
{
QgsLogger::warning( tr( "Adding a geometry feature is not supported in multiple edit mode" ) );
return;
@@ -425,7 +434,10 @@ void QgsRelationEditorWidget::updateUi()
if ( !isVisible() )
return;

if ( mFeatureList.size() == 1 )
mFormViewButton->setVisible( !multiEditModeActive() );
mTableViewButton->setVisible( !multiEditModeActive() );

if ( !multiEditModeActive() )
{
mStackedWidget->setCurrentWidget( mDualView );

@@ -456,27 +468,33 @@ void QgsRelationEditorWidget::updateUi()
else
{
mStackedWidget->setCurrentWidget( mMultiEditStackedWidgetPage ) ;
mMultiEditInformationLabel->setText( tr( "Simultaneously editing of %1 features." ).arg( mFeatureList.size() ) );

mMultiEditTreeWidget->clear();
for ( const QgsFeature &feature : mFeatureList )
for ( const QgsFeature &feature : std::as_const( mFeatureList ) )
{
QTreeWidgetItem *treeWidgetItem = new QTreeWidgetItem( mMultiEditTreeWidget );
treeWidgetItem->setText( 0, QString::number( feature.id() ) );
treeWidgetItem->setData( 0, static_cast<int>( MultiEditTreeWidgetRole::FeatureType ), static_cast<int>( MultiEditFeatureType::Parent ) );
treeWidgetItem->setText( 0, QgsVectorLayerUtils::getFeatureDisplayString( mRelation.referencedLayer(), feature ) );
treeWidgetItem->setIcon( 0, QgsIconUtils::iconForLayer( mRelation.referencedLayer() ) );

// Parent feature items are not selectable
treeWidgetItem->setFlags( Qt::ItemIsEnabled );

// Get child features
QgsFeatureRequest request = relation().getRelatedFeaturesRequest( feature );
QgsFeatureIterator fit = mRelation.referencingLayer()->getFeatures( request );
QgsFeatureIterator featureIterator = mRelation.referencingLayer()->getFeatures( request );
QgsFeature featureChild;
while ( fit.nextFeature( featureChild ) )
while ( featureIterator.nextFeature( featureChild ) )
{
QTreeWidgetItem *treeWidgetItemChild = new QTreeWidgetItem( treeWidgetItem );
treeWidgetItemChild->setText( 0, QString::number( featureChild.id() ) );
treeWidgetItemChild->setData( 0, static_cast<int>( MultiEditTreeWidgetRole::FeatureType ), static_cast<int>( MultiEditFeatureType::Child ) );
treeWidgetItemChild->setData( 0, static_cast<int>( MultiEditTreeWidgetRole::FeatureId ), featureChild.id() );
treeWidgetItemChild->setText( 0, QgsVectorLayerUtils::getFeatureDisplayString( mRelation.referencingLayer(), featureChild ) );
treeWidgetItemChild->setIcon( 0, QgsIconUtils::iconForLayer( mRelation.referencingLayer() ) );
treeWidgetItem->addChild( treeWidgetItemChild );
}

treeWidgetItem->setExpanded( true );
mMultiEditTreeWidget->addTopLevelItem( treeWidgetItem );
}
}
@@ -549,6 +567,24 @@ void QgsRelationEditorWidget::unsetMapTool()
disconnect( mMapToolDigitize, &QgsMapToolDigitizeFeature::digitizingCompleted, this, &QgsRelationEditorWidget::onDigitizingCompleted );
}

QgsFeatureIds QgsRelationEditorWidget::selectedChildFeatureIds() const
{
if ( multiEditModeActive() )
{
QgsFeatureIds featureIds;
for ( QTreeWidgetItem *treeWidgetItem : mMultiEditTreeWidget->selectedItems() )
{
if ( static_cast<MultiEditFeatureType>( treeWidgetItem->data( 0, static_cast<int>( MultiEditTreeWidgetRole::FeatureType ) ).toInt() ) != MultiEditFeatureType::Child )
continue;

featureIds.insert( treeWidgetItem->data( 0, static_cast<int>( MultiEditTreeWidgetRole::FeatureId ) ).toLongLong() );
}
return featureIds;
}
else
return mFeatureSelectionMgr->selectedFeatureIds();
}

void QgsRelationEditorWidget::onKeyPressed( QKeyEvent *e )
{
if ( e->key() == Qt::Key_Escape )
@@ -655,7 +691,8 @@ QgsIFeatureSelectionManager *QgsRelationEditorWidget::featureSelectionManager()

void QgsRelationEditorWidget::unlinkSelectedFeatures()
{
unlinkFeatures( mFeatureSelectionMgr->selectedFeatureIds() );
const QgsFeatureIds selectedFids = selectedChildFeatureIds();
unlinkFeatures( selectedFids );
}

void QgsRelationEditorWidget::duplicateFeature()
@@ -670,7 +707,7 @@ void QgsRelationEditorWidget::duplicateSelectedFeatures()

void QgsRelationEditorWidget::deleteSelectedFeatures()
{
const QgsFeatureIds selectedFids = mFeatureSelectionMgr->selectedFeatureIds();
const QgsFeatureIds selectedFids = selectedChildFeatureIds();
deleteFeatures( selectedFids );
}

@@ -212,6 +212,19 @@ class GUI_EXPORT QgsRelationEditorWidget : public QgsAbstractRelationEditorWidge
void initDualView( QgsVectorLayer *layer, const QgsFeatureRequest &request );
void setMapTool( QgsMapTool *mapTool );
void unsetMapTool();
QgsFeatureIds selectedChildFeatureIds() const;

enum class MultiEditFeatureType : int
{
Parent,
Child
};

enum class MultiEditTreeWidgetRole : int
{
FeatureType = Qt::UserRole + 1,
FeatureId = Qt::UserRole + 2
};

QgsDualView *mDualView = nullptr;
QPointer<QgsMessageBarItem> mMessageBarItem;
@@ -230,7 +243,6 @@ class GUI_EXPORT QgsRelationEditorWidget : public QgsAbstractRelationEditorWidge
QToolButton *mAddFeatureGeometryButton = nullptr;
QStackedWidget *mStackedWidget = nullptr;
QWidget *mMultiEditStackedWidgetPage = nullptr;
QLabel *mMultiEditInformationLabel = nullptr;
QTreeWidget *mMultiEditTreeWidget = nullptr;
QObjectUniquePtr<QgsMapToolDigitizeFeature> mMapToolDigitize;
QButtonGroup *mViewModeButtonGroup = nullptr;

0 comments on commit 90d3062

Please sign in to comment.