QGIS API Documentation  3.17.0-Master (8af46bc54f)
qgsrelationeditorwidget.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgsrelationeditor.cpp
3  --------------------------------------
4  Date : 17.5.2013
5  Copyright : (C) 2013 Matthias Kuhn
6  Email : matthias at opengis dot ch
7  ***************************************************************************
8  * *
9  * This program is free software; you can redistribute it and/or modify *
10  * it under the terms of the GNU General Public License as published by *
11  * the Free Software Foundation; either version 2 of the License, or *
12  * (at your option) any later version. *
13  * *
14  ***************************************************************************/
15 
17 
18 #include "qgsapplication.h"
19 #include "qgsdistancearea.h"
20 #include "qgsfeatureiterator.h"
21 #include "qgsvectordataprovider.h"
22 #include "qgsexpression.h"
23 #include "qgsfeature.h"
24 #include "qgsfeatureselectiondlg.h"
26 #include "qgsrelation.h"
27 #include "qgsvectorlayertools.h"
28 #include "qgsproject.h"
29 #include "qgstransactiongroup.h"
30 #include "qgslogger.h"
31 #include "qgsvectorlayerutils.h"
32 #include "qgsmapcanvas.h"
36 #include "qgsmessagebar.h"
37 #include "qgsmessagebaritem.h"
38 
39 #include <QHBoxLayout>
40 #include <QLabel>
41 #include <QMessageBox>
42 #include <QPushButton>
43 
46 QgsFilteredSelectionManager::QgsFilteredSelectionManager( QgsVectorLayer *layer, const QgsFeatureRequest &request, QObject *parent )
47  : QgsVectorLayerSelectionManager( layer, parent )
48  , mRequest( request )
49 {
50  if ( ! layer )
51  return;
52 
53  for ( auto fid : layer->selectedFeatureIds() )
54  if ( mRequest.acceptFeature( layer->getFeature( fid ) ) )
55  mSelectedFeatureIds << fid;
56 
57  connect( layer, &QgsVectorLayer::selectionChanged, this, &QgsFilteredSelectionManager::onSelectionChanged );
58 }
59 
60 const QgsFeatureIds &QgsFilteredSelectionManager::selectedFeatureIds() const
61 {
62  return mSelectedFeatureIds;
63 }
64 
65 int QgsFilteredSelectionManager::selectedFeatureCount()
66 {
67  return mSelectedFeatureIds.count();
68 }
69 
70 void QgsFilteredSelectionManager::onSelectionChanged( const QgsFeatureIds &selected, const QgsFeatureIds &deselected, bool clearAndSelect )
71 {
72  QgsFeatureIds lselected = selected;
73  if ( clearAndSelect )
74  {
75  mSelectedFeatureIds.clear();
76  }
77  else
78  {
79  for ( auto fid : deselected )
80  mSelectedFeatureIds.remove( fid );
81  }
82 
83  for ( auto fid : selected )
84  if ( mRequest.acceptFeature( layer()->getFeature( fid ) ) )
85  mSelectedFeatureIds << fid;
86  else
87  lselected.remove( fid );
88 
89  emit selectionChanged( lselected, deselected, clearAndSelect );
90 }
91 
93 
95  : QgsCollapsibleGroupBox( parent )
96 {
97  QVBoxLayout *topLayout = new QVBoxLayout( this );
98  topLayout->setContentsMargins( 0, 9, 0, 0 );
99  setLayout( topLayout );
100 
101  // buttons
102  QHBoxLayout *buttonLayout = new QHBoxLayout();
103  buttonLayout->setContentsMargins( 0, 0, 0, 0 );
104  // toggle editing
105  mToggleEditingButton = new QToolButton( this );
106  mToggleEditingButton->setObjectName( QStringLiteral( "mToggleEditingButton" ) );
107  mToggleEditingButton->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mActionToggleEditing.svg" ) ) );
108  mToggleEditingButton->setText( tr( "Toggle Editing" ) );
109  mToggleEditingButton->setEnabled( false );
110  mToggleEditingButton->setCheckable( true );
111  mToggleEditingButton->setToolTip( tr( "Toggle editing mode for child layer" ) );
112  buttonLayout->addWidget( mToggleEditingButton );
113  // save Edits
114  mSaveEditsButton = new QToolButton( this );
115  mSaveEditsButton->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mActionSaveEdits.svg" ) ) );
116  mSaveEditsButton->setText( tr( "Save Child Layer Edits" ) );
117  mSaveEditsButton->setToolTip( tr( "Save child layer edits" ) );
118  mSaveEditsButton->setEnabled( true );
119  buttonLayout->addWidget( mSaveEditsButton );
120  // add feature with geometry
121  mAddFeatureGeometryButton = new QToolButton( this );
122  mAddFeatureGeometryButton->setObjectName( QStringLiteral( "mAddFeatureGeometryButton" ) );
123  buttonLayout->addWidget( mAddFeatureGeometryButton );
124  // add feature
125  mAddFeatureButton = new QToolButton( this );
126  mAddFeatureButton->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mActionNewTableRow.svg" ) ) );
127  mAddFeatureButton->setText( tr( "Add Child Feature" ) );
128  mAddFeatureButton->setToolTip( tr( "Add child feature" ) );
129  mAddFeatureButton->setObjectName( QStringLiteral( "mAddFeatureButton" ) );
130  buttonLayout->addWidget( mAddFeatureButton );
131  // duplicate feature
132  mDuplicateFeatureButton = new QToolButton( this );
133  mDuplicateFeatureButton->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mActionDuplicateFeature.svg" ) ) );
134  mDuplicateFeatureButton->setText( tr( "Duplicate Child Feature" ) );
135  mDuplicateFeatureButton->setToolTip( tr( "Duplicate child feature" ) );
136  mDuplicateFeatureButton->setObjectName( QStringLiteral( "mDuplicateFeatureButton" ) );
137  buttonLayout->addWidget( mDuplicateFeatureButton );
138  // delete feature
139  mDeleteFeatureButton = new QToolButton( this );
140  mDeleteFeatureButton->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mActionDeleteSelected.svg" ) ) );
141  mDeleteFeatureButton->setText( tr( "Delete Child Feature" ) );
142  mDeleteFeatureButton->setToolTip( tr( "Delete child feature" ) );
143  mDeleteFeatureButton->setObjectName( QStringLiteral( "mDeleteFeatureButton" ) );
144  buttonLayout->addWidget( mDeleteFeatureButton );
145  // link feature
146  mLinkFeatureButton = new QToolButton( this );
147  mLinkFeatureButton->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mActionLink.svg" ) ) );
148  mLinkFeatureButton->setText( tr( "Link Existing Features" ) );
149  mLinkFeatureButton->setToolTip( tr( "Link existing child features" ) );
150  mLinkFeatureButton->setObjectName( QStringLiteral( "mLinkFeatureButton" ) );
151  buttonLayout->addWidget( mLinkFeatureButton );
152  // unlink feature
153  mUnlinkFeatureButton = new QToolButton( this );
154  mUnlinkFeatureButton->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mActionUnlink.svg" ) ) );
155  mUnlinkFeatureButton->setText( tr( "Unlink Feature" ) );
156  mUnlinkFeatureButton->setToolTip( tr( "Unlink child feature" ) );
157  mUnlinkFeatureButton->setObjectName( QStringLiteral( "mUnlinkFeatureButton" ) );
158  buttonLayout->addWidget( mUnlinkFeatureButton );
159  // zoom to linked feature
160  mZoomToFeatureButton = new QToolButton( this );
161  mZoomToFeatureButton->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mActionZoomToSelected.svg" ) ) );
162  mZoomToFeatureButton->setText( tr( "Zoom To Feature" ) );
163  mZoomToFeatureButton->setToolTip( tr( "Zoom to child feature" ) );
164  mZoomToFeatureButton->setObjectName( QStringLiteral( "mZoomToFeatureButton" ) );
165  buttonLayout->addWidget( mZoomToFeatureButton );
166  // spacer
167  buttonLayout->addItem( new QSpacerItem( 0, 0, QSizePolicy::Expanding ) );
168  // form view
169  mFormViewButton = new QToolButton( this );
170  mFormViewButton->setText( tr( "Form View" ) );
171  mFormViewButton->setToolTip( tr( "Switch to form view" ) );
172  mFormViewButton->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mActionPropertyItem.svg" ) ) );
173  mFormViewButton->setCheckable( true );
174  mFormViewButton->setChecked( mViewMode == QgsDualView::AttributeEditor );
175  buttonLayout->addWidget( mFormViewButton );
176  // table view
177  mTableViewButton = new QToolButton( this );
178  mTableViewButton->setText( tr( "Table View" ) );
179  mTableViewButton->setToolTip( tr( "Switch to table view" ) );
180  mTableViewButton->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mActionOpenTable.svg" ) ) );
181  mTableViewButton->setCheckable( true );
182  mTableViewButton->setChecked( mViewMode == QgsDualView::AttributeTable );
183  buttonLayout->addWidget( mTableViewButton );
184  // button group
185  mViewModeButtonGroup = new QButtonGroup( this );
186  mViewModeButtonGroup->addButton( mFormViewButton, QgsDualView::AttributeEditor );
187  mViewModeButtonGroup->addButton( mTableViewButton, QgsDualView::AttributeTable );
188 
189  // add buttons layout
190  topLayout->addLayout( buttonLayout );
191 
192  mRelationLayout = new QGridLayout();
193  mRelationLayout->setContentsMargins( 0, 0, 0, 0 );
194  topLayout->addLayout( mRelationLayout );
195 
196  mDualView = new QgsDualView( this );
197  mDualView->setView( mViewMode );
198 
199  mRelationLayout->addWidget( mDualView );
200 
201  connect( this, &QgsCollapsibleGroupBoxBasic::collapsedStateChanged, this, &QgsRelationEditorWidget::onCollapsedStateChanged );
202  connect( mViewModeButtonGroup, static_cast<void ( QButtonGroup::* )( int )>( &QButtonGroup::buttonClicked ),
203  this, static_cast<void ( QgsRelationEditorWidget::* )( int )>( &QgsRelationEditorWidget::setViewMode ) );
204  connect( mToggleEditingButton, &QAbstractButton::clicked, this, &QgsRelationEditorWidget::toggleEditing );
205  connect( mSaveEditsButton, &QAbstractButton::clicked, this, &QgsRelationEditorWidget::saveEdits );
206  connect( mAddFeatureButton, &QAbstractButton::clicked, this, [this]() { addFeature(); } );
207  connect( mAddFeatureGeometryButton, &QAbstractButton::clicked, this, &QgsRelationEditorWidget::addFeatureGeometry );
208  connect( mDuplicateFeatureButton, &QAbstractButton::clicked, this, &QgsRelationEditorWidget::duplicateFeature );
209  connect( mDeleteFeatureButton, &QAbstractButton::clicked, this, &QgsRelationEditorWidget::deleteSelectedFeatures );
210  connect( mLinkFeatureButton, &QAbstractButton::clicked, this, &QgsRelationEditorWidget::linkFeature );
211  connect( mUnlinkFeatureButton, &QAbstractButton::clicked, this, &QgsRelationEditorWidget::unlinkSelectedFeatures );
212  connect( mZoomToFeatureButton, &QAbstractButton::clicked, this, &QgsRelationEditorWidget::zoomToSelectedFeatures );
213 
214  connect( mDualView, &QgsDualView::showContextMenuExternally, this, &QgsRelationEditorWidget::showContextMenu );
215 
216  // Set initial state for add/remove etc. buttons
217  updateButtons();
218 }
219 
221 {
222  if ( mRelation.isValid() )
223  {
224  disconnect( mRelation.referencingLayer(), &QgsVectorLayer::editingStarted, this, &QgsRelationEditorWidget::updateButtons );
225  disconnect( mRelation.referencingLayer(), &QgsVectorLayer::editingStopped, this, &QgsRelationEditorWidget::updateButtons );
226  }
227 
228  mRelation = relation;
229  mFeature = feature;
230 
231  connect( mRelation.referencingLayer(), &QgsVectorLayer::editingStarted, this, &QgsRelationEditorWidget::updateButtons );
232  connect( mRelation.referencingLayer(), &QgsVectorLayer::editingStopped, this, &QgsRelationEditorWidget::updateButtons );
233 
234  updateTitle();
235 
236  QgsVectorLayer *lyr = relation.referencingLayer();
237 
238  bool canChangeAttributes = lyr->dataProvider()->capabilities() & QgsVectorDataProvider::ChangeAttributeValues;
239  if ( canChangeAttributes && !lyr->readOnly() )
240  {
241  mToggleEditingButton->setEnabled( true );
242  updateButtons();
243  }
244  else
245  {
246  mToggleEditingButton->setEnabled( false );
247  }
248 
249  setObjectName( QStringLiteral( "referenced/" ) + mRelation.name() );
250 
251  // If not yet initialized, it is not (yet) visible, so we don't load it to be faster (lazy loading)
252  // If it is already initialized, it has been set visible before and the currently shown feature is changing
253  // and the widget needs updating
254 
255  if ( mVisible )
256  {
257  QgsFeatureRequest myRequest = mRelation.getRelatedFeaturesRequest( mFeature );
258  initDualView( mRelation.referencingLayer(), myRequest );
259  }
260 }
261 
262 void QgsRelationEditorWidget::initDualView( QgsVectorLayer *layer, const QgsFeatureRequest &request )
263 {
264  QgsAttributeEditorContext ctx { mEditorContext };
265  ctx.setParentFormFeature( mFeature );
266  mDualView->init( layer, mEditorContext.mapCanvas(), request, ctx );
267  mFeatureSelectionMgr = new QgsFilteredSelectionManager( layer, request, mDualView );
268  mDualView->setFeatureSelectionManager( mFeatureSelectionMgr );
269 
270  connect( mFeatureSelectionMgr, &QgsIFeatureSelectionManager::selectionChanged, this, &QgsRelationEditorWidget::updateButtons );
271 
272  QIcon icon;
273  QString text;
274  if ( layer->geometryType() == QgsWkbTypes::PointGeometry )
275  {
276  icon = QgsApplication::getThemeIcon( QStringLiteral( "/mActionCapturePoint.svg" ) );
277  text = tr( "Add Point child Feature" );
278  }
279  else if ( layer->geometryType() == QgsWkbTypes::LineGeometry )
280  {
281  icon = QgsApplication::getThemeIcon( QStringLiteral( "/mActionCaptureLine.svg" ) );
282  text = tr( "Add Line child Feature" );
283  }
284  else if ( layer->geometryType() == QgsWkbTypes::PolygonGeometry )
285  {
286  icon = QgsApplication::getThemeIcon( QStringLiteral( "/mActionCapturePolygon.svg" ) );
287  text = tr( "Add Polygon Feature" );
288  }
289 
290  mAddFeatureGeometryButton->setIcon( icon );
291  mAddFeatureGeometryButton->setText( text );
292  mAddFeatureGeometryButton->setToolTip( text );
293 
294  updateButtons();
295 }
296 
297 void QgsRelationEditorWidget::setRelations( const QgsRelation &relation, const QgsRelation &nmrelation )
298 {
299  if ( mRelation.isValid() )
300  {
301  disconnect( mRelation.referencingLayer(), &QgsVectorLayer::editingStarted, this, &QgsRelationEditorWidget::updateButtons );
302  disconnect( mRelation.referencingLayer(), &QgsVectorLayer::editingStopped, this, &QgsRelationEditorWidget::updateButtons );
303  }
304 
305  if ( mNmRelation.isValid() )
306  {
307  disconnect( mNmRelation.referencedLayer(), &QgsVectorLayer::editingStarted, this, &QgsRelationEditorWidget::updateButtons );
308  disconnect( mNmRelation.referencedLayer(), &QgsVectorLayer::editingStopped, this, &QgsRelationEditorWidget::updateButtons );
309  }
310 
311  mRelation = relation;
312  mNmRelation = nmrelation;
313 
314  if ( !mRelation.isValid() )
315  return;
316 
317  mLayerInSameTransactionGroup = false;
318 
319  const auto transactionGroups = QgsProject::instance()->transactionGroups();
320  for ( auto it = transactionGroups.constBegin(); it != transactionGroups.constEnd(); ++it )
321  {
322  if ( mNmRelation.isValid() )
323  {
324  if ( it.value()->layers().contains( mRelation.referencedLayer() ) &&
325  it.value()->layers().contains( mRelation.referencingLayer() ) &&
326  it.value()->layers().contains( mNmRelation.referencedLayer() ) )
327  mLayerInSameTransactionGroup = true;
328  }
329  else
330  {
331  if ( it.value()->layers().contains( mRelation.referencedLayer() ) &&
332  it.value()->layers().contains( mRelation.referencingLayer() ) )
333  mLayerInSameTransactionGroup = true;
334  }
335  }
336 
337  connect( mRelation.referencingLayer(), &QgsVectorLayer::editingStarted, this, &QgsRelationEditorWidget::updateButtons );
338  connect( mRelation.referencingLayer(), &QgsVectorLayer::editingStopped, this, &QgsRelationEditorWidget::updateButtons );
339 
340  if ( mNmRelation.isValid() )
341  {
342  connect( mNmRelation.referencedLayer(), &QgsVectorLayer::editingStarted, this, &QgsRelationEditorWidget::updateButtons );
343  connect( mNmRelation.referencedLayer(), &QgsVectorLayer::editingStopped, this, &QgsRelationEditorWidget::updateButtons );
344  }
345 
346  updateTitle();
347 
348  QgsVectorLayer *lyr = relation.referencingLayer();
349 
350  bool canChangeAttributes = lyr->dataProvider()->capabilities() & QgsVectorDataProvider::ChangeAttributeValues;
351  if ( canChangeAttributes && !lyr->readOnly() )
352  {
353  mToggleEditingButton->setEnabled( true );
354  }
355  else
356  {
357  mToggleEditingButton->setEnabled( false );
358  }
359 
360  updateButtons();
361 
362  setObjectName( QStringLiteral( "referenced/" ) + mRelation.name() );
363 
364  updateUi();
365 }
366 
368 {
369  mEditorContext = context;
370 
371  if ( context.mapCanvas() && context.cadDockWidget() )
372  {
373  mMapToolDigitize.reset( new QgsMapToolDigitizeFeature( context.mapCanvas(), context.cadDockWidget() ) );
374  mMapToolDigitize->setButton( mAddFeatureGeometryButton );
375  }
376 
377  updateButtons();
378 }
379 
381 {
382  return mEditorContext;
383 }
384 
386 {
387  return mFeatureSelectionMgr;
388 }
389 
391 {
392  mDualView->setView( mode );
393  mViewMode = mode;
394 }
395 
397 {
398  mFeature = feature;
399 
400  mEditorContext.setFormFeature( feature );
401 
402  if ( update )
403  updateUi();
404 }
405 
406 void QgsRelationEditorWidget::updateButtons()
407 {
408  bool editable = false;
409  bool linkable = false;
410  bool spatial = false;
411  bool selectionNotEmpty = mFeatureSelectionMgr ? mFeatureSelectionMgr->selectedFeatureCount() : false;
412 
413  if ( mRelation.isValid() )
414  {
415  editable = mRelation.referencingLayer()->isEditable();
416  linkable = mRelation.referencingLayer()->isEditable();
417  spatial = mRelation.referencingLayer()->isSpatial();
418  }
419 
420  if ( mNmRelation.isValid() )
421  {
422  editable = mNmRelation.referencedLayer()->isEditable();
423  spatial = mNmRelation.referencedLayer()->isSpatial();
424  }
425 
426  mAddFeatureButton->setEnabled( editable );
427  mAddFeatureGeometryButton->setEnabled( editable );
428  mDuplicateFeatureButton->setEnabled( editable && selectionNotEmpty );
429  mLinkFeatureButton->setEnabled( linkable );
430  mDeleteFeatureButton->setEnabled( editable && selectionNotEmpty );
431  mUnlinkFeatureButton->setEnabled( linkable && selectionNotEmpty );
432  mZoomToFeatureButton->setEnabled( selectionNotEmpty );
433  mToggleEditingButton->setChecked( editable );
434  mSaveEditsButton->setEnabled( editable );
435 
436  mToggleEditingButton->setVisible( !mLayerInSameTransactionGroup );
437  mLinkFeatureButton->setVisible( mButtonsVisibility.testFlag( QgsAttributeEditorRelation::Button::Link ) );
438  mUnlinkFeatureButton->setVisible( mButtonsVisibility.testFlag( QgsAttributeEditorRelation::Button::Unlink ) );
439  mSaveEditsButton->setVisible( mButtonsVisibility.testFlag( QgsAttributeEditorRelation::Button::SaveChildEdits ) && !mLayerInSameTransactionGroup );
440  mAddFeatureButton->setVisible( mButtonsVisibility.testFlag( QgsAttributeEditorRelation::Button::AddChildFeature ) );
441  mAddFeatureGeometryButton->setVisible( mButtonsVisibility.testFlag( QgsAttributeEditorRelation::Button::AddChildFeature ) && mEditorContext.mapCanvas() && mEditorContext.cadDockWidget() && spatial );
442  mDuplicateFeatureButton->setVisible( mButtonsVisibility.testFlag( QgsAttributeEditorRelation::Button::DuplicateChildFeature ) );
443  mDeleteFeatureButton->setVisible( mButtonsVisibility.testFlag( QgsAttributeEditorRelation::Button::DeleteChildFeature ) );
444  mZoomToFeatureButton->setVisible( mButtonsVisibility.testFlag( QgsAttributeEditorRelation::Button::ZoomToChildFeature ) && mEditorContext.mapCanvas() && spatial );
445 }
446 
447 void QgsRelationEditorWidget::addFeatureGeometry()
448 {
449  QgsVectorLayer *layer = nullptr;
450  if ( mNmRelation.isValid() )
451  layer = mNmRelation.referencedLayer();
452  else
453  layer = mRelation.referencingLayer();
454 
455  mMapToolDigitize->setLayer( layer );
456 
457  // window is always on top, so we hide it to digitize without seeing it
458  window()->setVisible( false );
459  setMapTool( mMapToolDigitize );
460 
461  connect( mMapToolDigitize, &QgsMapToolDigitizeFeature::digitizingCompleted, this, &QgsRelationEditorWidget::onDigitizingCompleted );
462  connect( mEditorContext.mapCanvas(), &QgsMapCanvas::keyPressed, this, &QgsRelationEditorWidget::onKeyPressed );
463 
464  if ( auto *lMainMessageBar = mEditorContext.mainMessageBar() )
465  {
466  QString displayString = QgsVectorLayerUtils::getFeatureDisplayString( layer, mFeature );
467 
468  QString title = tr( "Create child feature for parent %1 \"%2\"" ).arg( mRelation.referencedLayer()->name(), displayString );
469  QString msg = tr( "Digitize the geometry for the new feature on layer %1. Press &lt;ESC&gt; to cancel." )
470  .arg( layer->name() );
471  mMessageBarItem = QgsMessageBar::createMessage( title, msg, this );
472  lMainMessageBar->pushItem( mMessageBarItem );
473  }
474 
475 }
476 
477 void QgsRelationEditorWidget::addFeature( const QgsGeometry &geometry )
478 {
479  QgsAttributeMap keyAttrs;
480 
481  const QgsVectorLayerTools *vlTools = mEditorContext.vectorLayerTools();
482 
483  if ( mNmRelation.isValid() )
484  {
485  // n:m Relation: first let the user create a new feature on the other table
486  // and autocreate a new linking feature.
487  QgsFeature f;
488  if ( vlTools->addFeature( mNmRelation.referencedLayer(), QgsAttributeMap(), geometry, &f ) )
489  {
490  // Fields of the linking table
491  const QgsFields fields = mRelation.referencingLayer()->fields();
492 
493  // Expression context for the linking table
495 
496  QgsAttributeMap linkAttributes;
497  const auto constFieldPairs = mRelation.fieldPairs();
498  for ( const QgsRelation::FieldPair &fieldPair : constFieldPairs )
499  {
500  int index = fields.indexOf( fieldPair.first );
501  linkAttributes.insert( index, mFeature.attribute( fieldPair.second ) );
502  }
503 
504  const auto constNmFieldPairs = mNmRelation.fieldPairs();
505  for ( const QgsRelation::FieldPair &fieldPair : constNmFieldPairs )
506  {
507  int index = fields.indexOf( fieldPair.first );
508  linkAttributes.insert( index, f.attribute( fieldPair.second ) );
509  }
510  QgsFeature linkFeature = QgsVectorLayerUtils::createFeature( mRelation.referencingLayer(), QgsGeometry(), linkAttributes, &context );
511 
512  mRelation.referencingLayer()->addFeature( linkFeature );
513 
514  updateUi();
515  }
516  }
517  else
518  {
519  QgsFields fields = mRelation.referencingLayer()->fields();
520 
521  const auto constFieldPairs = mRelation.fieldPairs();
522  for ( const QgsRelation::FieldPair &fieldPair : constFieldPairs )
523  {
524  keyAttrs.insert( fields.indexFromName( fieldPair.referencingField() ), mFeature.attribute( fieldPair.referencedField() ) );
525  }
526 
527  vlTools->addFeature( mDualView->masterModel()->layer(), keyAttrs, geometry );
528  }
529 }
530 
531 void QgsRelationEditorWidget::onDigitizingCompleted( const QgsFeature &feature )
532 {
533  addFeature( feature.geometry() );
534 
535  unsetMapTool();
536 }
537 
538 void QgsRelationEditorWidget::linkFeature()
539 {
540  QgsVectorLayer *layer = nullptr;
541 
542  if ( mNmRelation.isValid() )
543  layer = mNmRelation.referencedLayer();
544  else
545  layer = mRelation.referencingLayer();
546 
547  QgsFeatureSelectionDlg *selectionDlg = new QgsFeatureSelectionDlg( layer, mEditorContext, this );
548  selectionDlg->setAttribute( Qt::WA_DeleteOnClose );
549 
550  const QString displayString = QgsVectorLayerUtils::getFeatureDisplayString( mRelation.referencedLayer(), mFeature );
551  selectionDlg->setWindowTitle( tr( "Link existing child features for parent %1 \"%2\"" ).arg( mRelation.referencedLayer()->name(), displayString ) );
552 
553  connect( selectionDlg, &QDialog::accepted, this, &QgsRelationEditorWidget::onLinkFeatureDlgAccepted );
554  selectionDlg->show();
555 }
556 
557 void QgsRelationEditorWidget::onLinkFeatureDlgAccepted()
558 {
559  QgsFeatureSelectionDlg *selectionDlg = qobject_cast<QgsFeatureSelectionDlg *>( sender() );
560  if ( mNmRelation.isValid() )
561  {
562  QgsFeatureIterator it = mNmRelation.referencedLayer()->getFeatures(
564  .setFilterFids( selectionDlg->selectedFeatures() )
565  .setSubsetOfAttributes( mNmRelation.referencedFields() ) );
566 
567  QgsFeature relatedFeature;
568 
569  QgsFeatureList newFeatures;
570 
571  // Fields of the linking table
572  const QgsFields fields = mRelation.referencingLayer()->fields();
573 
574  // Expression context for the linking table
576 
577  QgsAttributeMap linkAttributes;
578  const auto constFieldPairs = mRelation.fieldPairs();
579  for ( const QgsRelation::FieldPair &fieldPair : constFieldPairs )
580  {
581  int index = fields.indexOf( fieldPair.first );
582  linkAttributes.insert( index, mFeature.attribute( fieldPair.second ) );
583  }
584 
585  while ( it.nextFeature( relatedFeature ) )
586  {
587  const auto constFieldPairs = mNmRelation.fieldPairs();
588  for ( const QgsRelation::FieldPair &fieldPair : constFieldPairs )
589  {
590  int index = fields.indexOf( fieldPair.first );
591  linkAttributes.insert( index, relatedFeature.attribute( fieldPair.second ) );
592  }
593  const QgsFeature linkFeature = QgsVectorLayerUtils::createFeature( mRelation.referencingLayer(), QgsGeometry(), linkAttributes, &context );
594 
595  newFeatures << linkFeature;
596  }
597 
598  mRelation.referencingLayer()->addFeatures( newFeatures );
599  QgsFeatureIds ids;
600  const auto constNewFeatures = newFeatures;
601  for ( const QgsFeature &f : constNewFeatures )
602  ids << f.id();
603  mRelation.referencingLayer()->selectByIds( ids );
604  }
605  else
606  {
607  QMap<int, QVariant> keys;
608  const auto constFieldPairs = mRelation.fieldPairs();
609  for ( const QgsRelation::FieldPair &fieldPair : constFieldPairs )
610  {
611  int idx = mRelation.referencingLayer()->fields().lookupField( fieldPair.referencingField() );
612  QVariant val = mFeature.attribute( fieldPair.referencedField() );
613  keys.insert( idx, val );
614  }
615 
616  const auto constSelectedFeatures = selectionDlg->selectedFeatures();
617  for ( QgsFeatureId fid : constSelectedFeatures )
618  {
619  QMapIterator<int, QVariant> it( keys );
620  while ( it.hasNext() )
621  {
622  it.next();
623  mRelation.referencingLayer()->changeAttributeValue( fid, it.key(), it.value() );
624  }
625  }
626  }
627 
628  updateUi();
629 }
630 
631 void QgsRelationEditorWidget::duplicateFeature()
632 {
633  QgsVectorLayer *layer = mRelation.referencingLayer();
634 
635  QgsFeatureIterator fit = layer->getFeatures( QgsFeatureRequest().setFilterFids( mFeatureSelectionMgr->selectedFeatureIds() ) );
636  QgsFeature f;
637  while ( fit.nextFeature( f ) )
638  {
639  QgsVectorLayerUtils::QgsDuplicateFeatureContext duplicatedFeatureContext;
640  QgsVectorLayerUtils::duplicateFeature( layer, f, QgsProject::instance(), duplicatedFeatureContext );
641  }
642 }
643 
644 void QgsRelationEditorWidget::deleteFeature( const QgsFeatureId featureid )
645 {
646  deleteFeatures( QgsFeatureIds() << featureid );
647 }
648 
649 void QgsRelationEditorWidget::deleteSelectedFeatures()
650 {
651  QgsFeatureIds selectedFids = mFeatureSelectionMgr->selectedFeatureIds();
652  deleteFeatures( selectedFids );
653 }
654 
655 void QgsRelationEditorWidget::deleteFeatures( const QgsFeatureIds &featureids )
656 {
657  bool deleteFeatures = true;
658 
659  QgsVectorLayer *layer;
660  if ( mNmRelation.isValid() )
661  {
662  layer = mNmRelation.referencedLayer();
663 
664  // When deleting a linked feature within an N:M relation,
665  // check if the feature is linked to more than just one feature.
666  // In case it is linked more than just once, ask the user for confirmation
667  // as it is likely he was not aware of the implications and might delete
668  // there may be several linking entries deleted along.
669 
670  QgsFeatureRequest deletedFeaturesRequest;
671  deletedFeaturesRequest.setFilterFids( featureids );
672  deletedFeaturesRequest.setFlags( QgsFeatureRequest::NoGeometry );
673  deletedFeaturesRequest.setSubsetOfAttributes( QgsAttributeList() << mNmRelation.referencedFields().first() );
674 
675  QgsFeatureIterator deletedFeatures = layer->getFeatures( deletedFeaturesRequest );
676  QStringList deletedFeaturesPks;
678  while ( deletedFeatures.nextFeature( feature ) )
679  {
680  deletedFeaturesPks.append( QgsExpression::quotedValue( feature.attribute( mNmRelation.referencedFields().first() ) ) );
681  }
682 
683  QgsFeatureRequest linkingFeaturesRequest;
684  linkingFeaturesRequest.setFlags( QgsFeatureRequest::NoGeometry );
685  linkingFeaturesRequest.setNoAttributes();
686 
687  QString linkingFeaturesRequestExpression;
688  if ( !deletedFeaturesPks.empty() )
689  {
690  linkingFeaturesRequestExpression = QStringLiteral( "%1 IN (%2)" ).arg( QgsExpression::quotedColumnRef( mNmRelation.fieldPairs().first().first ), deletedFeaturesPks.join( ',' ) );
691  linkingFeaturesRequest.setFilterExpression( linkingFeaturesRequestExpression );
692 
693  QgsFeatureIterator relatedLinkingFeatures = mNmRelation.referencingLayer()->getFeatures( linkingFeaturesRequest );
694 
695  int relatedLinkingFeaturesCount = 0;
696  while ( relatedLinkingFeatures.nextFeature( feature ) )
697  {
698  relatedLinkingFeaturesCount++;
699  }
700 
701  if ( deletedFeaturesPks.size() == 1 && relatedLinkingFeaturesCount > 1 )
702  {
703  QMessageBox messageBox( QMessageBox::Question, tr( "Really delete entry?" ), tr( "The entry on %1 is still linked to %2 features on %3. Do you want to delete it?" ).arg( mNmRelation.referencedLayer()->name(), QString::number( relatedLinkingFeaturesCount ), mRelation.referencedLayer()->name() ), QMessageBox::NoButton, this );
704  messageBox.addButton( QMessageBox::Cancel );
705  QAbstractButton *deleteButton = messageBox.addButton( tr( "Delete" ), QMessageBox::AcceptRole );
706 
707  messageBox.exec();
708  if ( messageBox.clickedButton() != deleteButton )
709  deleteFeatures = false;
710  }
711  else if ( deletedFeaturesPks.size() > 1 && relatedLinkingFeaturesCount > deletedFeaturesPks.size() )
712  {
713  QMessageBox messageBox( QMessageBox::Question, tr( "Really delete entries?" ), tr( "The %1 entries on %2 are still linked to %3 features on %4. Do you want to delete them?" ).arg( QString::number( deletedFeaturesPks.size() ), mNmRelation.referencedLayer()->name(), QString::number( relatedLinkingFeaturesCount ), mRelation.referencedLayer()->name() ), QMessageBox::NoButton, this );
714  messageBox.addButton( QMessageBox::Cancel );
715  QAbstractButton *deleteButton = messageBox.addButton( tr( "Delete" ), QMessageBox::AcceptRole );
716 
717  messageBox.exec();
718  if ( messageBox.clickedButton() != deleteButton )
719  deleteFeatures = false;
720  }
721  }
722  }
723  else
724  {
725  layer = mRelation.referencingLayer();
726  }
727 
729  if ( QgsVectorLayerUtils::impactsCascadeFeatures( layer, featureids, QgsProject::instance(), infoContext ) )
730  {
731  QString childrenInfo;
732  int childrenCount = 0;
733  const auto infoContextLayers = infoContext.layers();
734  for ( QgsVectorLayer *chl : infoContextLayers )
735  {
736  childrenCount += infoContext.duplicatedFeatures( chl ).size();
737  childrenInfo += ( tr( "%1 feature(s) on layer \"%2\", " ).arg( infoContext.duplicatedFeatures( chl ).size() ).arg( chl->name() ) );
738  }
739 
740  // for extra safety to make sure we know that the delete can have impact on children and joins
741  int res = QMessageBox::question( this, tr( "Delete at least %1 feature(s) on other layer(s)" ).arg( childrenCount ),
742  tr( "Delete %1 feature(s) on layer \"%2\", %3 as well\nand all of its other descendants.\nDelete these features?" ).arg( featureids.count() ).arg( layer->name() ).arg( childrenInfo ),
743  QMessageBox::Yes | QMessageBox::No );
744  if ( res != QMessageBox::Yes )
745  deleteFeatures = false;
746  }
747 
748  if ( deleteFeatures )
749  {
751  layer->deleteFeatures( featureids, &context );
752  const auto contextLayers = context.handledLayers();
753  if ( contextLayers.size() > 1 )
754  {
755  int deletedCount = 0;
756  QString feedbackMessage;
757  for ( QgsVectorLayer *contextLayer : contextLayers )
758  {
759  feedbackMessage += tr( "%1 on layer %2. " ).arg( context.handledFeatures( contextLayer ).size() ).arg( contextLayer->name() );
760  deletedCount += context.handledFeatures( contextLayer ).size();
761  }
762  mEditorContext.mainMessageBar()->pushMessage( tr( "%1 features deleted: %2" ).arg( deletedCount ).arg( feedbackMessage ), Qgis::Success );
763  }
764 
765  updateUi();
766  }
767 }
768 
769 void QgsRelationEditorWidget::unlinkFeature( const QgsFeatureId featureid )
770 {
771  unlinkFeatures( QgsFeatureIds() << featureid );
772 }
773 
774 void QgsRelationEditorWidget::unlinkSelectedFeatures()
775 {
776  unlinkFeatures( mFeatureSelectionMgr->selectedFeatureIds() );
777 }
778 
779 void QgsRelationEditorWidget::zoomToSelectedFeatures()
780 {
781  QgsMapCanvas *c = mEditorContext.mapCanvas();
782  if ( !c )
783  return;
784 
785  c->zoomToFeatureIds(
786  mNmRelation.isValid() ? mNmRelation.referencedLayer() : mRelation.referencingLayer(),
787  mFeatureSelectionMgr->selectedFeatureIds()
788  );
789 }
790 
791 void QgsRelationEditorWidget::unlinkFeatures( const QgsFeatureIds &featureids )
792 {
793  if ( mNmRelation.isValid() )
794  {
795  QgsFeatureIterator selectedIterator = mNmRelation.referencedLayer()->getFeatures(
797  .setFilterFids( featureids )
798  .setSubsetOfAttributes( mNmRelation.referencedFields() ) );
799 
800  QgsFeature f;
801 
802  QStringList filters;
803 
804  while ( selectedIterator.nextFeature( f ) )
805  {
806  filters << '(' + mNmRelation.getRelatedFeaturesRequest( f ).filterExpression()->expression() + ')';
807  }
808 
809  QString filter = QStringLiteral( "(%1) AND (%2)" ).arg(
810  mRelation.getRelatedFeaturesRequest( mFeature ).filterExpression()->expression(),
811  filters.join( QLatin1String( " OR " ) ) );
812 
813  QgsFeatureIterator linkedIterator = mRelation.referencingLayer()->getFeatures( QgsFeatureRequest()
814  .setNoAttributes()
815  .setFilterExpression( filter ) );
816 
817  QgsFeatureIds fids;
818 
819  while ( linkedIterator.nextFeature( f ) )
820  {
821  fids << f.id();
822  QgsDebugMsgLevel( FID_TO_STRING( f.id() ), 4 );
823  }
824 
825  mRelation.referencingLayer()->deleteFeatures( fids );
826 
827  updateUi();
828  }
829  else
830  {
831  QMap<int, QgsField> keyFields;
832  const auto constFieldPairs = mRelation.fieldPairs();
833  for ( const QgsRelation::FieldPair &fieldPair : constFieldPairs )
834  {
835  int idx = mRelation.referencingLayer()->fields().lookupField( fieldPair.referencingField() );
836  if ( idx < 0 )
837  {
838  QgsDebugMsg( QStringLiteral( "referencing field %1 not found" ).arg( fieldPair.referencingField() ) );
839  return;
840  }
841  QgsField fld = mRelation.referencingLayer()->fields().at( idx );
842  keyFields.insert( idx, fld );
843  }
844 
845  const auto constFeatureids = featureids;
846  for ( QgsFeatureId fid : constFeatureids )
847  {
848  QMapIterator<int, QgsField> it( keyFields );
849  while ( it.hasNext() )
850  {
851  it.next();
852  mRelation.referencingLayer()->changeAttributeValue( fid, it.key(), QVariant( it.value().type() ) );
853  }
854  }
855  }
856 }
857 
858 void QgsRelationEditorWidget::toggleEditing( bool state )
859 {
860  if ( state )
861  {
862  mEditorContext.vectorLayerTools()->startEditing( mRelation.referencingLayer() );
863  if ( mNmRelation.isValid() )
864  mEditorContext.vectorLayerTools()->startEditing( mNmRelation.referencedLayer() );
865  }
866  else
867  {
868  mEditorContext.vectorLayerTools()->stopEditing( mRelation.referencingLayer() );
869  if ( mNmRelation.isValid() )
870  mEditorContext.vectorLayerTools()->stopEditing( mNmRelation.referencedLayer() );
871  }
872 
873  updateButtons();
874 }
875 
876 void QgsRelationEditorWidget::saveEdits()
877 {
878  mEditorContext.vectorLayerTools()->saveEdits( mRelation.referencingLayer() );
879  if ( mNmRelation.isValid() )
880  mEditorContext.vectorLayerTools()->saveEdits( mNmRelation.referencedLayer() );
881 }
882 
883 void QgsRelationEditorWidget::onCollapsedStateChanged( bool collapsed )
884 {
885  if ( !collapsed )
886  {
887  mVisible = true;
888  updateUi();
889  }
890 }
891 
892 void QgsRelationEditorWidget::updateUi()
893 {
894  // If not yet initialized, it is not (yet) visible, so we don't load it to be faster (lazy loading)
895  // If it is already initialized, it has been set visible before and the currently shown feature is changing
896  // and the widget needs updating
897 
898  if ( mVisible )
899  {
900  QgsFeatureRequest myRequest = mRelation.getRelatedFeaturesRequest( mFeature );
901 
902  if ( mNmRelation.isValid() )
903  {
904  QgsFeatureIterator it = mRelation.referencingLayer()->getFeatures( myRequest );
905 
906  QgsFeature fet;
907 
908  QStringList filters;
909 
910  while ( it.nextFeature( fet ) )
911  {
912  QString filter = mNmRelation.getReferencedFeatureRequest( fet ).filterExpression()->expression();
913  filters << filter.prepend( '(' ).append( ')' );
914  }
915 
916  QgsFeatureRequest nmRequest;
917 
918  nmRequest.setFilterExpression( filters.join( QLatin1String( " OR " ) ) );
919 
920  initDualView( mNmRelation.referencedLayer(), nmRequest );
921  }
922  else if ( mRelation.referencingLayer() )
923  {
924  initDualView( mRelation.referencingLayer(), myRequest );
925  }
926  }
927 }
928 
930 {
931  return mLinkFeatureButton->isVisible();
932 }
933 
935 {
936  mLinkFeatureButton->setVisible( showLinkButton );
937 }
938 
940 {
941  return mUnlinkFeatureButton->isVisible();
942 }
943 
945 {
946  mSaveEditsButton->setVisible( showChildEdits );
947 }
948 
950 {
951  return mSaveEditsButton->isVisible();
952 }
953 
954 void QgsRelationEditorWidget::setVisibleButtons( const QgsAttributeEditorRelation::Buttons &buttons )
955 {
956  mButtonsVisibility = buttons;
957  updateButtons();
958 }
959 
960 QgsAttributeEditorRelation::Buttons QgsRelationEditorWidget::visibleButtons() const
961 {
962  QgsAttributeEditorRelation::Buttons buttons;
963  if ( mLinkFeatureButton->isVisible() )
964  buttons |= QgsAttributeEditorRelation::Button::Link;
965  if ( mUnlinkFeatureButton->isVisible() )
966  buttons |= QgsAttributeEditorRelation::Button::Unlink;
967  if ( mSaveEditsButton->isVisible() )
968  buttons |= QgsAttributeEditorRelation::Button::SaveChildEdits;
969  if ( mAddFeatureButton->isVisible() )
970  buttons |= QgsAttributeEditorRelation::Button::AddChildFeature;
971  if ( mDuplicateFeatureButton->isVisible() )
972  buttons |= QgsAttributeEditorRelation::Button::DuplicateChildFeature;
973  if ( mDeleteFeatureButton->isVisible() )
974  buttons |= QgsAttributeEditorRelation::Button::DeleteChildFeature;
975  if ( mZoomToFeatureButton->isVisible() )
976  buttons |= QgsAttributeEditorRelation::Button::ZoomToChildFeature;
977  return buttons;
978 }
979 
981 {
982  mForceSuppressFormPopup = forceSuppressFormPopup;
983 }
984 
986 {
987  return mForceSuppressFormPopup;
988 }
989 
991 {
992  mNmRelationId = nmRelationId;
993 }
994 
996 {
997  return mNmRelationId;
998 }
999 
1001 {
1002  return mLabel;
1003 }
1004 
1006 {
1007  mLabel = label;
1008 
1009  updateTitle();
1010 }
1011 
1013 {
1014  mUnlinkFeatureButton->setVisible( showUnlinkButton );
1015 }
1016 
1017 void QgsRelationEditorWidget::parentFormValueChanged( const QString &attribute, const QVariant &newValue )
1018 {
1019  mDualView->parentFormValueChanged( attribute, newValue );
1020 }
1021 
1023 {
1024  return mShowLabel;
1025 }
1026 
1028 {
1029  mShowLabel = showLabel;
1030 
1031  updateTitle();
1032 }
1033 
1034 void QgsRelationEditorWidget::showContextMenu( QgsActionMenu *menu, const QgsFeatureId fid )
1035 {
1036  if ( mRelation.referencingLayer()->isEditable() )
1037  {
1038  QAction *qAction = nullptr;
1039 
1040  qAction = menu->addAction( QgsApplication::getThemeIcon( QStringLiteral( "/mActionDeleteSelected.svg" ) ), tr( "Delete Feature" ) );
1041  connect( qAction, &QAction::triggered, this, [this, fid]() { deleteFeature( fid ); } );
1042 
1043  qAction = menu->addAction( QgsApplication::getThemeIcon( QStringLiteral( "/mActionUnlink.svg" ) ), tr( "Unlink Feature" ) );
1044  connect( qAction, &QAction::triggered, this, [this, fid]() { unlinkFeature( fid ); } );
1045  }
1046 }
1047 
1048 void QgsRelationEditorWidget::setMapTool( QgsMapTool *mapTool )
1049 {
1050  QgsMapCanvas *mapCanvas = mEditorContext.mapCanvas();
1051 
1052  mapCanvas->setMapTool( mapTool );
1053  mapCanvas->window()->raise();
1054  mapCanvas->activateWindow();
1055  mapCanvas->setFocus();
1056  connect( mapTool, &QgsMapTool::deactivated, this, &QgsRelationEditorWidget::mapToolDeactivated );
1057 }
1058 
1059 void QgsRelationEditorWidget::unsetMapTool()
1060 {
1061  QgsMapCanvas *mapCanvas = mEditorContext.mapCanvas();
1062 
1063  // this will call mapToolDeactivated
1064  mapCanvas->unsetMapTool( mMapToolDigitize );
1065 
1066  disconnect( mapCanvas, &QgsMapCanvas::keyPressed, this, &QgsRelationEditorWidget::onKeyPressed );
1067  disconnect( mMapToolDigitize, &QgsMapToolDigitizeFeature::digitizingCompleted, this, &QgsRelationEditorWidget::onDigitizingCompleted );
1068 }
1069 
1070 void QgsRelationEditorWidget::updateTitle()
1071 {
1072  if ( mShowLabel && !mLabel.isEmpty() )
1073  {
1074  setTitle( mLabel );
1075  }
1076  else if ( mShowLabel && mRelation.isValid() )
1077  {
1078  setTitle( mRelation.name() );
1079  }
1080  else
1081  {
1082  setTitle( QString() );
1083  }
1084 }
1085 
1087 {
1088  return mFeature;
1089 }
1090 
1091 void QgsRelationEditorWidget::onKeyPressed( QKeyEvent *e )
1092 {
1093  if ( e->key() == Qt::Key_Escape )
1094  {
1095  unsetMapTool();
1096  }
1097 }
1098 
1099 void QgsRelationEditorWidget::mapToolDeactivated()
1100 {
1101  window()->setVisible( true );
1102  window()->raise();
1103  window()->activateWindow();
1104 
1105  if ( mEditorContext.mainMessageBar() && mMessageBarItem )
1106  {
1107  mEditorContext.mainMessageBar()->popWidget( mMessageBarItem );
1108  }
1109  mMessageBarItem = nullptr;
1110 }
void unsetMapTool(QgsMapTool *mapTool)
Unset the current map tool or last non zoom tool.
int lookupField(const QString &fieldName) const
Looks up field&#39;s index from the field name.
Definition: qgsfields.cpp:344
Methods in this class are used to handle basic operations on vector layers.
const QgsVectorLayerTools * vectorLayerTools() const
Returns the associated vector layer tools.
QString name
Definition: qgsrelation.h:48
QgsFeatureId id
Definition: qgsfeature.h:64
Q_DECL_DEPRECATED bool showUnlinkButton() const
Determines if the "unlink feature" button should be shown.
Wrapper for iterator of features from vector data provider or vector layer.
QgsVectorLayer * layer() const
Returns the layer this model uses as backend.
void setFeature(const QgsFeature &feature, bool update=true)
Sets the feature being edited and updates the UI unless update is set to false.
virtual bool saveEdits(QgsVectorLayer *layer) const =0
Should be called, when the features should be committed but the editing session is not ended...
QSet< QgsFeatureId > QgsFeatureIds
Definition: qgsfeatureid.h:37
void init(QgsVectorLayer *layer, QgsMapCanvas *mapCanvas, const QgsFeatureRequest &request=QgsFeatureRequest(), const QgsAttributeEditorContext &context=QgsAttributeEditorContext(), bool loadFeatures=true)
Has to be called to initialize the dual view.
bool deleteFeatures(const QgsFeatureIds &fids, DeleteContext *context=nullptr)
Deletes a set of features from the layer (but does not commit it)
bool collapsed
The collapsed state of this group box.
virtual QgsVectorDataProvider::Capabilities capabilities() const
Returns flags containing the supported capabilities.
static QString quotedColumnRef(QString name)
Returns a quoted column reference (in double quotes)
virtual bool startEditing(QgsVectorLayer *layer) const =0
This will be called, whenever a vector layer should be switched to edit mode.
QgsAttributeTableModel * masterModel() const
Returns the model which has the information about all features (not only filtered) ...
Definition: qgsdualview.h:182
void setFeatureSelectionManager(QgsIFeatureSelectionManager *featureSelectionManager)
Set the feature selection model.
A groupbox that collapses/expands when toggled and can save its collapsed and checked states...
QgsFeature feature() const
Returns the widget&#39;s current feature.
Contains mainly the QMap with QgsVectorLayer and QgsFeatureIds do list all the duplicated features...
QList< QgsVectorLayer * > layers() const
Returns all the layers on which features have been duplicated.
void parentFormValueChanged(const QString &attribute, const QVariant &newValue)
Called when an attribute value in the parent widget has changed to newValue.
#define QgsDebugMsg(str)
Definition: qgslogger.h:38
void setParentFormFeature(const QgsFeature &feature)
Sets the feature of the currently edited parent form.
This class contains context information for attribute editor widgets.
QList< QgsFeature > QgsFeatureList
Definition: qgsfeature.h:583
void setForceSuppressFormPopup(bool forceSuppressFormPopup)
Sets force suppress form popup status with forceSuppressFormPopup configured for this widget...
QgsFeature getFeature(QgsFeatureId fid) const
Queries the layer for the feature with the given id.
QgsVectorLayer referencingLayer
Definition: qgsrelation.h:46
void parentFormValueChanged(const QString &attribute, const QVariant &value)
Called in embedded forms when an attribute value in the parent form has changed.
void digitizingCompleted(const QgsFeature &feature)
Emitted whenever the digitizing has been successfully completed.
ViewMode
The view modes, in which this widget can present information.
Definition: qgsdualview.h:52
void collapsedStateChanged(bool collapsed)
Signal emitted when groupbox collapsed/expanded state is changed, and when first shown.
qint64 QgsFeatureId
64 bit feature ids negative numbers are used for uncommitted/newly added features ...
Definition: qgsfeatureid.h:28
QgsFeatureRequest & setSubsetOfAttributes(const QgsAttributeList &attrs)
Set a subset of attributes that will be fetched.
Q_INVOKABLE QgsWkbTypes::GeometryType geometryType() const
Returns point, line or polygon.
Container of fields for a vector layer.
Definition: qgsfields.h:44
A geometry is the spatial representation of a feature.
Definition: qgsgeometry.h:123
static QIcon getThemeIcon(const QString &name)
Helper to get a theme icon.
Q_DECL_DEPRECATED bool showLinkButton() const
Determines if the "link feature" button should be shown.
virtual bool stopEditing(QgsVectorLayer *layer, bool allowCancel=true) const =0
Will be called, when an editing session is ended and the features should be committed.
QgsMessageBar * mainMessageBar()
Returns the main message bar.
void selectionChanged(const QgsFeatureIds &selected, const QgsFeatureIds &deselected, bool clearAndSelect)
Emitted when selection was changed.
The feature class encapsulates a single feature including its id, geometry and a list of field/values...
Definition: qgsfeature.h:55
const QgsFeatureIds & selectedFeatureIds() const override
Returns reference to identifiers of selected features.
void setViewMode(QgsDualView::ViewMode mode)
Define the view mode for the dual view.
static QString getFeatureDisplayString(const QgsVectorLayer *layer, const QgsFeature &feature)
bool isSpatial() const FINAL
Returns true if this is a geometry layer and false in case of NoGeometry (table only) or UnknownGeome...
Map canvas is a class for displaying all GIS data types on a canvas.
Definition: qgsmapcanvas.h:84
QList< QgsVectorLayer * > handledLayers(bool includeAuxiliaryLayers=true) const
Returns a list of all layers affected by the delete operation.
void setFormFeature(const QgsFeature &feature)
Set current feature for the currently edited form or table row.
void reset(T *p=nullptr)
Will reset the managed pointer to p.
void setView(ViewMode view)
Change the current view mode.
void setButton(QAbstractButton *button)
Use this to associate a button to this maptool.
Definition: qgsmaptool.cpp:136
QgsField at(int i) const
Gets field at particular index (must be in range 0..N-1)
Definition: qgsfields.cpp:163
QgsExpression * filterExpression() const
Returns the filter expression if set.
Show a list of the features, where one can be chosen and the according attribute dialog will be prese...
Definition: qgsdualview.h:65
As part of the API refactoring and improvements which landed in the Processing API was substantially reworked from the x version This was done in order to allow much of the underlying Processing framework to be ported into c
bool isEditable() const FINAL
Returns true if the provider is in editing mode.
bool addFeatures(QgsFeatureList &features, QgsFeatureSink::Flags flags=QgsFeatureSink::Flags()) FINAL
Adds a list of features to the sink.
static QgsMessageBarItem * createMessage(const QString &text, QWidget *parent=nullptr)
Creates message bar item widget containing a message text to be displayed on the bar.
Q_DECL_DEPRECATED void setShowLinkButton(bool showLinkButton)
Determines if the "link feature" button should be shown.
QgsAttributeList referencedFields() const
Returns a list of attributes used to form the referenced fields (most likely primary key) on the refe...
QgsFeatureRequest & setFilterExpression(const QString &expression)
Set the filter expression.
void setMapTool(QgsMapTool *mapTool, bool clean=false)
Sets the map tool currently being used on the canvas.
int indexFromName(const QString &fieldName) const
Gets the field index from the field name.
Definition: qgsfields.cpp:202
QgsFeatureRequest getReferencedFeatureRequest(const QgsAttributes &attributes) const
Creates a request to return the feature on the referenced (parent) layer which is referenced by the p...
QgsFields fields() const FINAL
Returns the list of fields of this layer.
void setLayer(QgsMapLayer *vl)
Change the layer edited by the map tool.
Q_INVOKABLE const QgsFeatureIds & selectedFeatureIds() const
Returns a list of the selected features IDs in this layer.
Defines a relation between matching fields of the two involved tables of a relation.
Definition: qgsrelation.h:74
Shows the features and attributes in a table layout.
Definition: qgsdualview.h:58
#define QgsDebugMsgLevel(str, level)
Definition: qgslogger.h:39
Q_DECL_DEPRECATED void setShowUnlinkButton(bool showUnlinkButton)
Determines if the "unlink feature" button should be shown.
bool popWidget(QgsMessageBarItem *item)
Remove the specified item from the bar, and display the next most recent one in the stack...
QgsFeatureRequest & setNoAttributes()
Set that no attributes will be fetched.
This class is a menu that is populated automatically with the actions defined for a given layer...
Definition: qgsactionmenu.h:37
Expression contexts are used to encapsulate the parameters around which a QgsExpression should be eva...
static QgsFeature createFeature(const QgsVectorLayer *layer, const QgsGeometry &geometry=QgsGeometry(), const QgsAttributeMap &attributes=QgsAttributeMap(), QgsExpressionContext *context=nullptr)
Creates a new feature ready for insertion into a layer.
QMap< int, QVariant > QgsAttributeMap
Definition: qgsattributes.h:38
void editingStopped()
Emitted when edited changes have been successfully written to the data provider.
This class wraps a request for features to a vector layer (or directly its vector data provider)...
QgsRelationEditorWidget(QWidget *parent=nullptr)
void selectionChanged(const QgsFeatureIds &selected, const QgsFeatureIds &deselected, bool clearAndSelect)
Emitted when selection was changed.
QgsVectorLayer referencedLayer
Definition: qgsrelation.h:47
QString label() const
Determines the label of this element.
Encapsulate a field in an attribute table or data source.
Definition: qgsfield.h:49
QgsFeatureRequest getRelatedFeaturesRequest(const QgsFeature &feature) const
Creates a request to return all the features on the referencing (child) layer which have a foreign ke...
void editingStarted()
Emitted when editing on this layer has started.
QgsExpressionContext createExpressionContext() const FINAL
This method needs to be reimplemented in all classes which implement this interface and return an exp...
void keyPressed(QKeyEvent *e)
Emit key press event.
void pushMessage(const QString &text, Qgis::MessageLevel level=Qgis::Info, int duration=-1)
A convenience method for pushing a message with the specified text to the bar.
QgsAdvancedDigitizingDockWidget * cadDockWidget() const
Returns the associated CAD dock widget (e.g.
QList< QgsRelation::FieldPair > fieldPairs() const
Returns the field pairs which form this relation The first element of each pair are the field names o...
QgsAttributeEditorContext editorContext() const
Returns the attribute editor context.
QgsAttributeEditorRelation::Buttons visibleButtons() const
Returns the buttons which are shown.
QString expression() const
Returns the original, unmodified expression string.
QVariant nmRelationId() const
Determines the relation id of the second relation involved in an N:M relation.
void setRelations(const QgsRelation &relation, const QgsRelation &nmrelation)
Set the relation(s) for this widget If only one relation is set, it will act as a simple 1:N relation...
Q_DECL_DEPRECATED bool showSaveChildEditsButton() const
Determines if the "Save child layer edits" button should be shown.
void setLabel(const QString &label=QString())
Sets label for this element If it&#39;s empty it takes the relation id as label.
Abstract base class for all map tools.
Definition: qgsmaptool.h:63
void setRelationFeature(const QgsRelation &relation, const QgsFeature &feature)
Sets the relation and the feature.
void showContextMenuExternally(QgsActionMenu *menu, QgsFeatureId fid)
Emitted when selecting context menu on the feature list to create the context menu individually...
void zoomToFeatureIds(QgsVectorLayer *layer, const QgsFeatureIds &ids)
Set canvas extent to the bounding box of a set of features.
void deactivated()
signal emitted once the map tool is deactivated
bool isValid
Definition: qgsrelation.h:49
Q_DECL_DEPRECATED void setShowSaveChildEditsButton(bool showChildEdits)
Determines if the "Save child layer edits" button should be shown.
QgsFeatureRequest & setFilterFids(const QgsFeatureIds &fids)
Sets feature IDs that should be fetched.
Context for cascade delete features.
#define FID_TO_STRING(fid)
Definition: qgsfeatureid.h:33
int indexOf(const QString &fieldName) const
Gets the field index from the field name.
Definition: qgsfields.cpp:207
QgsFeatureIds handledFeatures(QgsVectorLayer *layer) const
Returns a list of feature IDs from the specified layer affected by the delete operation.
int selectedFeatureCount() override
Returns the number of features that are selected in this layer.
QMap< QPair< QString, QString >, QgsTransactionGroup * > transactionGroups()
Map of transaction groups.
QgsIFeatureSelectionManager * featureSelectionManager()
The feature selection manager is responsible for the selected features which are currently being edit...
void setShowLabel(bool showLabel)
Defines if a title label should be shown for this widget.
static QgsProject * instance()
Returns the QgsProject singleton instance.
Definition: qgsproject.cpp:469
QgsFeatureIds duplicatedFeatures(QgsVectorLayer *layer) const
Returns the duplicated features in the given layer.
void setEditorContext(const QgsAttributeEditorContext &context)
Sets the editor context.
static bool impactsCascadeFeatures(const QgsVectorLayer *layer, const QgsFeatureIds &fids, const QgsProject *project, QgsDuplicateFeatureContext &context, QgsVectorLayerUtils::CascadedFeatureFlags flags=QgsVectorLayerUtils::CascadedFeatureFlags())
Q_INVOKABLE void selectByIds(const QgsFeatureIds &ids, QgsVectorLayer::SelectBehavior behavior=QgsVectorLayer::SetSelection)
Selects matching features using a list of feature IDs.
virtual bool addFeature(QgsVectorLayer *layer, const QgsAttributeMap &defaultValues=QgsAttributeMap(), const QgsGeometry &defaultGeometry=QgsGeometry(), QgsFeature *feature=nullptr) const =0
This method should/will be called, whenever a new feature will be added to the layer.
This tool digitizes geometry of new point/line/polygon features on already existing vector layers Onc...
QgsFeatureIterator getFeatures(const QgsFeatureRequest &request=QgsFeatureRequest()) const FINAL
Queries the layer for features specified in request.
QString name
Definition: qgsmaplayer.h:87
bool addFeature(QgsFeature &feature, QgsFeatureSink::Flags flags=QgsFeatureSink::Flags()) FINAL
Adds a single feature to the sink.
bool changeAttributeValue(QgsFeatureId fid, int field, const QVariant &newValue, const QVariant &oldValue=QVariant(), bool skipDefaultValues=false)
Changes an attribute value for a feature (but does not immediately commit the changes).
QgsGeometry geometry
Definition: qgsfeature.h:67
QgsVectorDataProvider * dataProvider() FINAL
Returns the layer&#39;s data provider, it may be nullptr.
QList< int > QgsAttributeList
Definition: qgsfield.h:26
void setVisibleButtons(const QgsAttributeEditorRelation::Buttons &buttons)
Defines the buttons which are shown.
bool nextFeature(QgsFeature &f)
static QString quotedValue(const QVariant &value)
Returns a string representation of a literal value, including appropriate quotations where required...
static QgsFeature duplicateFeature(QgsVectorLayer *layer, const QgsFeature &feature, QgsProject *project, QgsDuplicateFeatureContext &duplicateFeatureContext, const int maxDepth=0, int depth=0, QList< QgsVectorLayer *> referencedLayersBranch=QList< QgsVectorLayer *>())
Duplicates a feature and it&#39;s children (one level deep).
void setNmRelationId(const QVariant &nmRelationId=QVariant())
Sets nmRelationId for the relation id of the second relation involved in an N:M relation.
Geometry is not required. It may still be returned if e.g. required for a filter condition.
Is an interface class to abstract feature selection handling.
Represents a vector layer which manages a vector based data sets.
QVariant attribute(const QString &name) const
Lookup attribute value from attribute name.
Definition: qgsfeature.cpp:264
QgsMapCanvas * mapCanvas() const
Returns the associated map canvas (e.g.
Allows modification of attribute values.
This widget is used to show the attributes of a set of features of a QgsVectorLayer.
Definition: qgsdualview.h:41
bool showLabel() const
Defines if a title label should be shown for this widget.
QgsFeatureRequest & setFlags(QgsFeatureRequest::Flags flags)
Sets flags that affect how features will be fetched.
bool forceSuppressFormPopup() const
Determines the force suppress form popup status that is configured for this widget.