QGIS API Documentation  3.25.0-Master (6b426f5f8a)
qgsrelationeditorwidget.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgsrelationeditorwidget.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 "qgsiconutils.h"
27 #include "qgsrelation.h"
28 #include "qgsvectorlayertools.h"
29 #include "qgsproject.h"
30 #include "qgstransactiongroup.h"
31 #include "qgslogger.h"
32 #include "qgsvectorlayerutils.h"
33 #include "qgsmapcanvas.h"
37 #include "qgsmessagebar.h"
38 #include "qgsmessagebaritem.h"
39 #include "qgscollapsiblegroupbox.h"
40 
41 #include <QHBoxLayout>
42 #include <QLabel>
43 #include <QMessageBox>
44 #include <QPushButton>
45 #include <QTreeWidget>
46 
49 QgsFilteredSelectionManager::QgsFilteredSelectionManager( QgsVectorLayer *layer, const QgsFeatureRequest &request, QObject *parent )
50  : QgsVectorLayerSelectionManager( layer, parent )
51  , mRequest( request )
52 {
53  if ( ! layer )
54  return;
55 
56  for ( const auto fid : layer->selectedFeatureIds() )
57  if ( mRequest.acceptFeature( layer->getFeature( fid ) ) )
58  mSelectedFeatureIds << fid;
59 
60  connect( layer, &QgsVectorLayer::selectionChanged, this, &QgsFilteredSelectionManager::onSelectionChanged );
61 }
62 
63 const QgsFeatureIds &QgsFilteredSelectionManager::selectedFeatureIds() const
64 
65 
66 {
67  return mSelectedFeatureIds;
68 }
69 
70 int QgsFilteredSelectionManager::selectedFeatureCount()
71 {
72  return mSelectedFeatureIds.count();
73 }
74 
75 void QgsFilteredSelectionManager::onSelectionChanged( const QgsFeatureIds &selected, const QgsFeatureIds &deselected, bool clearAndSelect )
76 {
77  QgsFeatureIds lselected = selected;
78  if ( clearAndSelect )
79  {
80  mSelectedFeatureIds.clear();
81  }
82  else
83  {
84  for ( const auto fid : deselected )
85  mSelectedFeatureIds.remove( fid );
86  }
87 
88  for ( const auto fid : selected )
89  if ( mRequest.acceptFeature( layer()->getFeature( fid ) ) )
90  mSelectedFeatureIds << fid;
91  else
92  lselected.remove( fid );
93 
94  emit selectionChanged( lselected, deselected, clearAndSelect );
95 }
96 
98 
99 QgsRelationEditorWidget::QgsRelationEditorWidget( const QVariantMap &config, QWidget *parent )
100  : QgsAbstractRelationEditorWidget( config, parent )
101  , mButtonsVisibility( qgsFlagKeysToValue( config.value( QStringLiteral( "buttons" ) ).toString(), QgsRelationEditorWidget::Button::AllButtons ) )
102  , mShowFirstFeature( config.value( QStringLiteral( "show_first_feature" ), true ).toBool() )
103 {
104  QVBoxLayout *rootLayout = new QVBoxLayout( this );
105  rootLayout->setContentsMargins( 0, 9, 0, 0 );
106 
107  // buttons
108  QHBoxLayout *buttonLayout = new QHBoxLayout();
109  buttonLayout->setContentsMargins( 0, 0, 0, 0 );
110  // toggle editing
111  mToggleEditingButton = new QToolButton( this );
112  mToggleEditingButton->setObjectName( QStringLiteral( "mToggleEditingButton" ) );
113  mToggleEditingButton->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mActionToggleEditing.svg" ) ) );
114  mToggleEditingButton->setText( tr( "Toggle Editing" ) );
115  mToggleEditingButton->setEnabled( false );
116  mToggleEditingButton->setCheckable( true );
117  mToggleEditingButton->setToolTip( tr( "Toggle editing mode for child layer" ) );
118  buttonLayout->addWidget( mToggleEditingButton );
119  // save Edits
120  mSaveEditsButton = new QToolButton( this );
121  mSaveEditsButton->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mActionSaveEdits.svg" ) ) );
122  mSaveEditsButton->setText( tr( "Save Child Layer Edits" ) );
123  mSaveEditsButton->setToolTip( tr( "Save child layer edits" ) );
124  mSaveEditsButton->setEnabled( true );
125  buttonLayout->addWidget( mSaveEditsButton );
126  // add feature with geometry
127  mAddFeatureGeometryButton = new QToolButton( this );
128  mAddFeatureGeometryButton->setObjectName( QStringLiteral( "mAddFeatureGeometryButton" ) );
129  buttonLayout->addWidget( mAddFeatureGeometryButton );
130  // add feature
131  mAddFeatureButton = new QToolButton( this );
132  mAddFeatureButton->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mActionNewTableRow.svg" ) ) );
133  mAddFeatureButton->setText( tr( "Add Child Feature" ) );
134  mAddFeatureButton->setToolTip( tr( "Add child feature" ) );
135  mAddFeatureButton->setObjectName( QStringLiteral( "mAddFeatureButton" ) );
136  buttonLayout->addWidget( mAddFeatureButton );
137  // duplicate feature
138  mDuplicateFeatureButton = new QToolButton( this );
139  mDuplicateFeatureButton->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mActionDuplicateFeature.svg" ) ) );
140  mDuplicateFeatureButton->setText( tr( "Duplicate Child Feature(s)" ) );
141  mDuplicateFeatureButton->setToolTip( tr( "Duplicate selected child feature(s)" ) );
142  mDuplicateFeatureButton->setObjectName( QStringLiteral( "mDuplicateFeatureButton" ) );
143  buttonLayout->addWidget( mDuplicateFeatureButton );
144  // delete feature
145  mDeleteFeatureButton = new QToolButton( this );
146  mDeleteFeatureButton->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mActionDeleteSelectedFeatures.svg" ) ) );
147  mDeleteFeatureButton->setText( tr( "Delete Child Feature(s)" ) );
148  mDeleteFeatureButton->setToolTip( tr( "Delete selected child feature(s)" ) );
149  mDeleteFeatureButton->setObjectName( QStringLiteral( "mDeleteFeatureButton" ) );
150  buttonLayout->addWidget( mDeleteFeatureButton );
151  // link feature
152  mLinkFeatureButton = new QToolButton( this );
153  mLinkFeatureButton->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mActionLink.svg" ) ) );
154  mLinkFeatureButton->setText( tr( "Link Existing Feature(s)" ) );
155  mLinkFeatureButton->setToolTip( tr( "Link existing child feature(s)" ) );
156  mLinkFeatureButton->setObjectName( QStringLiteral( "mLinkFeatureButton" ) );
157  buttonLayout->addWidget( mLinkFeatureButton );
158  // unlink feature
159  mUnlinkFeatureButton = new QToolButton( this );
160  mUnlinkFeatureButton->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mActionUnlink.svg" ) ) );
161  mUnlinkFeatureButton->setText( tr( "Unlink Feature(s)" ) );
162  mUnlinkFeatureButton->setToolTip( tr( "Unlink selected child feature(s)" ) );
163  mUnlinkFeatureButton->setObjectName( QStringLiteral( "mUnlinkFeatureButton" ) );
164  buttonLayout->addWidget( mUnlinkFeatureButton );
165  // zoom to linked feature
166  mZoomToFeatureButton = new QToolButton( this );
167  mZoomToFeatureButton->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mActionZoomToSelected.svg" ) ) );
168  mZoomToFeatureButton->setText( tr( "Zoom To Feature(s)" ) );
169  mZoomToFeatureButton->setToolTip( tr( "Zoom to selected child feature(s)" ) );
170  mZoomToFeatureButton->setObjectName( QStringLiteral( "mZoomToFeatureButton" ) );
171  buttonLayout->addWidget( mZoomToFeatureButton );
172  // spacer
173  buttonLayout->addItem( new QSpacerItem( 0, 0, QSizePolicy::Expanding ) );
174  // form view
175  mFormViewButton = new QToolButton( this );
176  mFormViewButton->setText( tr( "Form View" ) );
177  mFormViewButton->setToolTip( tr( "Switch to form view" ) );
178  mFormViewButton->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mActionPropertyItem.svg" ) ) );
179  mFormViewButton->setCheckable( true );
180  mFormViewButton->setChecked( mViewMode == QgsDualView::AttributeEditor );
181  buttonLayout->addWidget( mFormViewButton );
182  // table view
183  mTableViewButton = new QToolButton( this );
184  mTableViewButton->setText( tr( "Table View" ) );
185  mTableViewButton->setToolTip( tr( "Switch to table view" ) );
186  mTableViewButton->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mActionOpenTable.svg" ) ) );
187  mTableViewButton->setCheckable( true );
188  mTableViewButton->setChecked( mViewMode == QgsDualView::AttributeTable );
189  buttonLayout->addWidget( mTableViewButton );
190  // button group
191  mViewModeButtonGroup = new QButtonGroup( this );
192  mViewModeButtonGroup->addButton( mFormViewButton, QgsDualView::AttributeEditor );
193  mViewModeButtonGroup->addButton( mTableViewButton, QgsDualView::AttributeTable );
194  // multiedit info label
195  mMultiEditInfoLabel = new QLabel( this );
196  buttonLayout->addWidget( mMultiEditInfoLabel );
197 
198  // add buttons layout
199  rootLayout->addLayout( buttonLayout );
200 
201  // add stacked widget
202  mStackedWidget = new QStackedWidget( this );
203 
204  // add dual view (single feature content)
205  mDualView = new QgsDualView( this );
206  mDualView->setView( mViewMode );
207  connect( mDualView, &QgsDualView::showContextMenuExternally, this, &QgsRelationEditorWidget::showContextMenu );
208 
209  // add multi feature editing page
210  mMultiEditStackedWidgetPage = new QWidget( this );
211  {
212  QVBoxLayout *vBoxLayout = new QVBoxLayout();
213  vBoxLayout->setContentsMargins( 0, 0, 0, 0 );
214 
215  mMultiEditTreeWidget = new QTreeWidget( this );
216  mMultiEditTreeWidget->setHeaderHidden( true );
217  mMultiEditTreeWidget->setSelectionMode( QTreeWidget::ExtendedSelection );
218  vBoxLayout->addWidget( mMultiEditTreeWidget );
219 
220  mMultiEditStackedWidgetPage->setLayout( vBoxLayout );
221  }
222  mStackedWidget->addWidget( mMultiEditStackedWidgetPage );
223 
224  mStackedWidget->addWidget( mDualView );
225 
226  rootLayout->addWidget( mStackedWidget );
227 
228 #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
229  connect( mViewModeButtonGroup, static_cast<void ( QButtonGroup::* )( int )>( &QButtonGroup::buttonClicked ),
230  this, static_cast<void ( QgsRelationEditorWidget::* )( int )>( &QgsRelationEditorWidget::setViewMode ) );
231 #else
232  connect( mViewModeButtonGroup, &QButtonGroup::idClicked,
233  this, static_cast<void ( QgsRelationEditorWidget::* )( int )>( &QgsRelationEditorWidget::setViewMode ) );
234 #endif
235  connect( mToggleEditingButton, &QAbstractButton::clicked, this, &QgsRelationEditorWidget::toggleEditing );
236  connect( mSaveEditsButton, &QAbstractButton::clicked, this, &QgsRelationEditorWidget::saveEdits );
237  connect( mAddFeatureButton, &QAbstractButton::clicked, this, &QgsRelationEditorWidget::addFeature );
238  connect( mAddFeatureGeometryButton, &QAbstractButton::clicked, this, &QgsRelationEditorWidget::addFeatureGeometry );
239  connect( mDuplicateFeatureButton, &QAbstractButton::clicked, this, &QgsRelationEditorWidget::duplicateSelectedFeatures );
240  connect( mDeleteFeatureButton, &QAbstractButton::clicked, this, &QgsRelationEditorWidget::deleteSelectedFeatures );
241  connect( mLinkFeatureButton, &QAbstractButton::clicked, this, &QgsRelationEditorWidget::linkFeature );
242  connect( mUnlinkFeatureButton, &QAbstractButton::clicked, this, &QgsRelationEditorWidget::unlinkSelectedFeatures );
243  connect( mZoomToFeatureButton, &QAbstractButton::clicked, this, &QgsRelationEditorWidget::zoomToSelectedFeatures );
244  connect( mMultiEditTreeWidget, &QTreeWidget::itemSelectionChanged, this, &QgsRelationEditorWidget::multiEditItemSelectionChanged );
245 
246  // Set initial state for add/remove etc. buttons
247  updateButtons();
248 
249  setLayout( rootLayout );
250 }
251 
252 void QgsRelationEditorWidget::initDualView( QgsVectorLayer *layer, const QgsFeatureRequest &request )
253 {
254  if ( multiEditModeActive() )
255  {
256  QgsLogger::warning( tr( "Dual view should not be used in multiple edit mode" ) );
257  return;
258  }
259 
261  ctx.setParentFormFeature( mFeatureList.first() );
262  mDualView->init( layer, mEditorContext.mapCanvas(), request, ctx, true, mShowFirstFeature );
263  mFeatureSelectionMgr = new QgsFilteredSelectionManager( layer, request, mDualView );
264  mDualView->setFeatureSelectionManager( mFeatureSelectionMgr );
265 
266  connect( mFeatureSelectionMgr, &QgsIFeatureSelectionManager::selectionChanged, this, &QgsRelationEditorWidget::updateButtons );
267 
268  QIcon icon;
269  QString text;
270  if ( layer->geometryType() == QgsWkbTypes::PointGeometry )
271  {
272  icon = QgsApplication::getThemeIcon( QStringLiteral( "/mActionCapturePoint.svg" ) );
273  text = tr( "Add Point Child Feature" );
274  }
275  else if ( layer->geometryType() == QgsWkbTypes::LineGeometry )
276  {
277  icon = QgsApplication::getThemeIcon( QStringLiteral( "/mActionCaptureLine.svg" ) );
278  text = tr( "Add Line Child Feature" );
279  }
280  else if ( layer->geometryType() == QgsWkbTypes::PolygonGeometry )
281  {
282  icon = QgsApplication::getThemeIcon( QStringLiteral( "/mActionCapturePolygon.svg" ) );
283  text = tr( "Add Polygon Child Feature" );
284  }
285 
286  mAddFeatureGeometryButton->setIcon( icon );
287  mAddFeatureGeometryButton->setText( text );
288  mAddFeatureGeometryButton->setToolTip( text );
289 
290  updateButtons();
291 }
292 
294 {
295  mEditorContext = context;
296 
297  if ( context.mapCanvas() && context.cadDockWidget() )
298  {
299  mMapToolDigitize.reset( new QgsMapToolDigitizeFeature( context.mapCanvas(), context.cadDockWidget() ) );
300  mMapToolDigitize->setButton( mAddFeatureGeometryButton );
301  }
302 
303  updateButtons();
304 }
305 
307 {
308  mDualView->setView( mode );
309  mViewMode = mode;
310 }
311 
312 void QgsRelationEditorWidget::updateButtons()
313 {
314  bool toggleEditingButtonEnabled = false;
315  bool canAdd = false;
316  bool canAddGeometry = false;
317  bool canRemove = false;
318  bool canEdit = false;
319  bool canLink = false;
320  bool canUnlink = false;
321  bool spatial = false;
322 
323  if ( mRelation.isValid() )
324  {
325  toggleEditingButtonEnabled = mRelation.referencingLayer()->supportsEditing();
326  canAdd = mRelation.referencingLayer()->isEditable();
327  canAddGeometry = mRelation.referencingLayer()->isEditable();
328  canRemove = mRelation.referencingLayer()->isEditable();
329  canEdit = mRelation.referencingLayer()->isEditable();
330  canLink = mRelation.referencingLayer()->isEditable();
331  canUnlink = mRelation.referencingLayer()->isEditable();
332  spatial = mRelation.referencingLayer()->isSpatial();
333  }
334 
335  if ( mNmRelation.isValid() )
336  {
337  toggleEditingButtonEnabled |= mNmRelation.referencedLayer()->supportsEditing();
338  canAdd = mNmRelation.referencedLayer()->isEditable();
339  canAddGeometry = mNmRelation.referencedLayer()->isEditable();
340  canRemove = mNmRelation.referencedLayer()->isEditable();
341  canEdit = mNmRelation.referencedLayer()->isEditable();
342  spatial = mNmRelation.referencedLayer()->isSpatial();
343  }
344 
345  const bool selectionNotEmpty = mFeatureSelectionMgr ? mFeatureSelectionMgr->selectedFeatureCount() : false;
346  if ( multiEditModeActive() )
347  {
348  const bool multieditLinkedChildSelected = ! selectedChildFeatureIds().isEmpty();
349 
350  canAddGeometry = false;
351 
352  canRemove = canRemove && multieditLinkedChildSelected;
353 
354  // In 1:n relations an element can be linked only to 1 feature
355  canLink = canLink && mNmRelation.isValid();
356  canUnlink = canUnlink && multieditLinkedChildSelected;
357  }
358  else
359  {
360  canRemove = canRemove && selectionNotEmpty;
361  canUnlink = canUnlink && selectionNotEmpty;
362  }
363 
364  mToggleEditingButton->setEnabled( toggleEditingButtonEnabled );
365  mAddFeatureButton->setEnabled( canAdd );
366  mAddFeatureGeometryButton->setEnabled( canAddGeometry );
367  mDuplicateFeatureButton->setEnabled( canEdit && selectionNotEmpty );
368  mLinkFeatureButton->setEnabled( canLink );
369  mDeleteFeatureButton->setEnabled( canRemove );
370  mUnlinkFeatureButton->setEnabled( canUnlink );
371  mZoomToFeatureButton->setEnabled( selectionNotEmpty );
372  mToggleEditingButton->setChecked( canEdit );
373  mSaveEditsButton->setEnabled( canEdit || canLink || canUnlink );
374 
375  mToggleEditingButton->setVisible( !mLayerInSameTransactionGroup );
376 
377  mLinkFeatureButton->setVisible( mButtonsVisibility.testFlag( QgsRelationEditorWidget::Button::Link ) );
378  mUnlinkFeatureButton->setVisible( mButtonsVisibility.testFlag( QgsRelationEditorWidget::Button::Unlink ) );
379  mSaveEditsButton->setVisible( mButtonsVisibility.testFlag( QgsRelationEditorWidget::Button::SaveChildEdits ) && !mLayerInSameTransactionGroup );
380  mAddFeatureButton->setVisible( mButtonsVisibility.testFlag( QgsRelationEditorWidget::Button::AddChildFeature ) );
381  mAddFeatureGeometryButton->setVisible( mButtonsVisibility.testFlag( QgsRelationEditorWidget::Button::AddChildFeature ) && mEditorContext.mapCanvas() && mEditorContext.cadDockWidget() && spatial );
382  mDuplicateFeatureButton->setVisible( mButtonsVisibility.testFlag( QgsRelationEditorWidget::Button::DuplicateChildFeature ) );
383  mDeleteFeatureButton->setVisible( mButtonsVisibility.testFlag( QgsRelationEditorWidget::Button::DeleteChildFeature ) );
384  mZoomToFeatureButton->setVisible( mButtonsVisibility.testFlag( QgsRelationEditorWidget::Button::ZoomToChildFeature ) && mEditorContext.mapCanvas() && spatial );
385 }
386 
387 void QgsRelationEditorWidget::addFeature()
388 {
390 
391  if ( !multiEditModeActive() )
392  return;
393 
394  mMultiEditTreeWidget->blockSignals( true );
395  mMultiEdit1NJustAddedIds = addedFeatures;
396  QTreeWidgetItemIterator treeWidgetItemIterator( mMultiEditTreeWidget );
397  while ( *treeWidgetItemIterator )
398  {
399  if ( ( *treeWidgetItemIterator )->data( 0, static_cast<int>( MultiEditTreeWidgetRole::FeatureType ) ).toInt() != static_cast<int>( MultiEditFeatureType::Child ) )
400  {
401  ++treeWidgetItemIterator;
402  continue;
403  }
404 
405  if ( addedFeatures.contains( ( *treeWidgetItemIterator )->data( 0, static_cast<int>( MultiEditTreeWidgetRole::FeatureId ) ).toInt() ) )
406  ( *treeWidgetItemIterator )->setSelected( true );
407 
408  ++treeWidgetItemIterator;
409  }
410  mMultiEditTreeWidget->blockSignals( false );
411 
412  updateUi();
413  updateButtons();
414 }
415 
416 void QgsRelationEditorWidget::addFeatureGeometry()
417 {
418  if ( multiEditModeActive() )
419  {
420  QgsLogger::warning( tr( "Adding a geometry feature is not supported in multiple edit mode" ) );
421  return;
422  }
423 
424  QgsVectorLayer *layer = nullptr;
425  if ( mNmRelation.isValid() )
426  layer = mNmRelation.referencedLayer();
427  else
428  layer = mRelation.referencingLayer();
429 
430  mMapToolDigitize->setLayer( layer );
431 
432  // window is always on top, so we hide it to digitize without seeing it
433  if ( window()->objectName() != QStringLiteral( "QgisApp" ) )
434  {
435  window()->setVisible( false );
436  }
437  setMapTool( mMapToolDigitize );
438 
439  connect( mMapToolDigitize, &QgsMapToolDigitizeFeature::digitizingCompleted, this, &QgsRelationEditorWidget::onDigitizingCompleted );
440  connect( mEditorContext.mapCanvas(), &QgsMapCanvas::keyPressed, this, &QgsRelationEditorWidget::onKeyPressed );
441 
442  if ( auto *lMainMessageBar = mEditorContext.mainMessageBar() )
443  {
444  const QString displayString = QgsVectorLayerUtils::getFeatureDisplayString( layer, mFeatureList.first() );
445 
446  const QString title = tr( "Create child feature for parent %1 \"%2\"" ).arg( mRelation.referencedLayer()->name(), displayString );
447  const QString msg = tr( "Digitize the geometry for the new feature on layer %1. Press &lt;ESC&gt; to cancel." )
448  .arg( layer->name() );
449  mMessageBarItem = QgsMessageBar::createMessage( title, msg, this );
450  lMainMessageBar->pushItem( mMessageBarItem );
451  }
452 }
453 
454 void QgsRelationEditorWidget::onDigitizingCompleted( const QgsFeature &feature )
455 {
457 
458  unsetMapTool();
459 }
460 
461 void QgsRelationEditorWidget::multiEditItemSelectionChanged()
462 {
463  const QList<QTreeWidgetItem *> selectedItems = mMultiEditTreeWidget->selectedItems();
464 
465  // Select all items pointing to the same feature
466  // but only if we are not deselecting.
467  if ( selectedItems.size() == 1
468  && mMultiEditPreviousSelectedItems.size() <= 1 )
469  {
470  if ( selectedItems.first()->data( 0, static_cast<int>( MultiEditTreeWidgetRole::FeatureType ) ).toInt() == static_cast<int>( MultiEditFeatureType::Child ) )
471  {
472  mMultiEditTreeWidget->blockSignals( true );
473 
474  const QgsFeatureId featureIdSelectedItem = selectedItems.first()->data( 0, static_cast<int>( MultiEditTreeWidgetRole::FeatureId ) ).toInt();
475 
476  QTreeWidgetItemIterator treeWidgetItemIterator( mMultiEditTreeWidget );
477  while ( *treeWidgetItemIterator )
478  {
479  if ( ( *treeWidgetItemIterator )->data( 0, static_cast<int>( MultiEditTreeWidgetRole::FeatureType ) ).toInt() != static_cast<int>( MultiEditFeatureType::Child ) )
480  {
481  ++treeWidgetItemIterator;
482  continue;
483  }
484 
485  const QgsFeatureId featureIdCurrentItem = ( *treeWidgetItemIterator )->data( 0, static_cast<int>( MultiEditTreeWidgetRole::FeatureId ) ).toInt();
486  if ( mNmRelation.isValid() )
487  {
488  if ( featureIdSelectedItem == featureIdCurrentItem )
489  ( *treeWidgetItemIterator )->setSelected( true );
490  }
491  else
492  {
493  if ( ! mMultiEdit1NJustAddedIds.contains( featureIdSelectedItem ) )
494  break;
495 
496  if ( mMultiEdit1NJustAddedIds.contains( featureIdCurrentItem ) )
497  ( *treeWidgetItemIterator )->setSelected( true );
498  }
499 
500  ++treeWidgetItemIterator;
501  }
502  mMultiEditTreeWidget->blockSignals( false );
503  }
504  }
505  mMultiEditPreviousSelectedItems = selectedItems;
506  updateButtons();
507 }
508 
509 void QgsRelationEditorWidget::toggleEditing( bool state )
510 {
512 
513  updateButtons();
514 }
515 
517 {
518  if ( !mRelation.isValid() || mFeatureList.isEmpty() || !mFeatureList.first().isValid() )
519  return;
520 
521  if ( !isVisible() )
522  return;
523 
524  if ( multiEditModeActive() )
525  updateUiMultiEdit();
526  else
527  updateUiSingleEdit();
528 }
529 
530 void QgsRelationEditorWidget::setVisibleButtons( const Buttons &buttons )
531 {
532  mButtonsVisibility = buttons;
533  updateButtons();
534 }
535 
536 QgsRelationEditorWidget::Buttons QgsRelationEditorWidget::visibleButtons() const
537 {
538  Buttons buttons;
539  if ( mLinkFeatureButton->isVisible() )
540  buttons |= Button::Link;
541  if ( mUnlinkFeatureButton->isVisible() )
542  buttons |= Button::Unlink;
543  if ( mSaveEditsButton->isVisible() )
544  buttons |= Button::SaveChildEdits;
545  if ( mAddFeatureButton->isVisible() )
546  buttons |= Button::AddChildFeature;
547  if ( mDuplicateFeatureButton->isVisible() )
548  buttons |= Button::DuplicateChildFeature;
549  if ( mDeleteFeatureButton->isVisible() )
550  buttons |= Button::DeleteChildFeature;
551  if ( mZoomToFeatureButton->isVisible() )
552  buttons |= Button::ZoomToChildFeature;
553  return buttons;
554 }
555 
556 void QgsRelationEditorWidget::parentFormValueChanged( const QString &attribute, const QVariant &newValue )
557 {
558  mDualView->parentFormValueChanged( attribute, newValue );
559 }
560 
561 void QgsRelationEditorWidget::showContextMenu( QgsActionMenu *menu, const QgsFeatureId fid )
562 {
564  {
565  QAction *qAction = nullptr;
566 
567  qAction = menu->addAction( QgsApplication::getThemeIcon( QStringLiteral( "/mActionDeleteSelected.svg" ) ), tr( "Delete Feature" ) );
568  connect( qAction, &QAction::triggered, this, [this, fid]() { deleteFeature( fid ); } );
569 
570  qAction = menu->addAction( QgsApplication::getThemeIcon( QStringLiteral( "/mActionUnlink.svg" ) ), tr( "Unlink Feature" ) );
571  connect( qAction, &QAction::triggered, this, [this, fid]() { unlinkFeature( fid ); } );
572  }
573 }
574 
575 void QgsRelationEditorWidget::setMapTool( QgsMapTool *mapTool )
576 {
577  QgsMapCanvas *mapCanvas = mEditorContext.mapCanvas();
578 
579  mapCanvas->setMapTool( mapTool );
580  mapCanvas->window()->raise();
581  mapCanvas->activateWindow();
582  mapCanvas->setFocus();
583  connect( mapTool, &QgsMapTool::deactivated, this, &QgsRelationEditorWidget::mapToolDeactivated );
584 }
585 
586 void QgsRelationEditorWidget::unsetMapTool()
587 {
588  QgsMapCanvas *mapCanvas = mEditorContext.mapCanvas();
589 
590  // this will call mapToolDeactivated
591  mapCanvas->unsetMapTool( mMapToolDigitize );
592 
593  disconnect( mapCanvas, &QgsMapCanvas::keyPressed, this, &QgsRelationEditorWidget::onKeyPressed );
594  disconnect( mMapToolDigitize, &QgsMapToolDigitizeFeature::digitizingCompleted, this, &QgsRelationEditorWidget::onDigitizingCompleted );
595 }
596 
597 QgsFeatureIds QgsRelationEditorWidget::selectedChildFeatureIds() const
598 {
599  if ( multiEditModeActive() )
600  {
601  QgsFeatureIds featureIds;
602  for ( QTreeWidgetItem *treeWidgetItem : mMultiEditTreeWidget->selectedItems() )
603  {
604  if ( static_cast<MultiEditFeatureType>( treeWidgetItem->data( 0, static_cast<int>( MultiEditTreeWidgetRole::FeatureType ) ).toInt() ) != MultiEditFeatureType::Child )
605  continue;
606 
607  featureIds.insert( treeWidgetItem->data( 0, static_cast<int>( MultiEditTreeWidgetRole::FeatureId ) ).toLongLong() );
608  }
609  return featureIds;
610  }
611  else
612  return mFeatureSelectionMgr->selectedFeatureIds();
613 }
614 
615 void QgsRelationEditorWidget::updateUiSingleEdit()
616 {
617  mFormViewButton->setVisible( true );
618  mTableViewButton->setVisible( true );
619  mMultiEditInfoLabel->setVisible( false );
620 
621  mStackedWidget->setCurrentWidget( mDualView );
622 
624 
625  if ( mNmRelation.isValid() )
626  {
628  QgsFeature fet;
629  QStringList filters;
630 
631  while ( it.nextFeature( fet ) )
632  {
634  filters << filter.prepend( '(' ).append( ')' );
635  }
636 
637  QgsFeatureRequest nmRequest;
638  nmRequest.setFilterExpression( filters.join( QLatin1String( " OR " ) ) );
639 
640  initDualView( mNmRelation.referencedLayer(), nmRequest );
641  }
642  else if ( mRelation.referencingLayer() )
643  {
644  initDualView( mRelation.referencingLayer(), request );
645  }
646 }
647 
648 void QgsRelationEditorWidget::updateUiMultiEdit()
649 {
650  mFormViewButton->setVisible( false );
651  mTableViewButton->setVisible( false );
652  mMultiEditInfoLabel->setVisible( true );
653 
654  mStackedWidget->setCurrentWidget( mMultiEditStackedWidgetPage ) ;
655 
656  QList<QTreeWidgetItem *> parentTreeWidgetItems;
657 
658  QgsFeatureIds featureIdsMixedValues;
659  QMultiMap<QTreeWidgetItem *, QgsFeatureId> multimapChildFeatures;
660 
661  mMultiEditTreeWidget->clear();
662  for ( const QgsFeature &featureParent : std::as_const( mFeatureList ) )
663  {
664  QTreeWidgetItem *treeWidgetItem = createMultiEditTreeWidgetItem( featureParent, mRelation.referencedLayer(), MultiEditFeatureType::Parent );
665 
666  // Parent feature items are not selectable
667  treeWidgetItem->setFlags( Qt::ItemIsEnabled );
668 
669  parentTreeWidgetItems.append( treeWidgetItem );
670 
671  // Get child features
672  const QgsFeatureRequest request = relation().getRelatedFeaturesRequest( featureParent );
673  QgsFeatureIterator featureIterator = mRelation.referencingLayer()->getFeatures( request );
674  QgsFeature featureChild;
675  while ( featureIterator.nextFeature( featureChild ) )
676  {
677  if ( mNmRelation.isValid() )
678  {
679  const QgsFeatureRequest requestFinalChild = mNmRelation.getReferencedFeatureRequest( featureChild );
680  QgsFeatureIterator featureIteratorFinalChild = mNmRelation.referencedLayer()->getFeatures( requestFinalChild );
681  QgsFeature featureChildChild;
682  while ( featureIteratorFinalChild.nextFeature( featureChildChild ) )
683  {
684  QTreeWidgetItem *treeWidgetItemChild = createMultiEditTreeWidgetItem( featureChildChild, mNmRelation.referencedLayer(), MultiEditFeatureType::Child );
685 
686  treeWidgetItem->addChild( treeWidgetItemChild );
687 
688  featureIdsMixedValues.insert( featureChildChild.id() );
689  multimapChildFeatures.insert( treeWidgetItem, featureChildChild.id() );
690  }
691  }
692  else
693  {
694  QTreeWidgetItem *treeWidgetItemChild = createMultiEditTreeWidgetItem( featureChild, mRelation.referencingLayer(), MultiEditFeatureType::Child );
695  treeWidgetItem->addChild( treeWidgetItemChild );
696 
697  featureIdsMixedValues.insert( featureChild.id() );
698  }
699  }
700 
701  mMultiEditTreeWidget->addTopLevelItem( treeWidgetItem );
702  treeWidgetItem->setExpanded( true );
703  }
704 
705  // Set mixed values indicator (Green or Orange)
706  //
707  // Green:
708  // n:m and 1:n: 0 child features available
709  // n:m with no mixed values
710  // Orange:
711  // n:m with mixed values
712  // 1:n always, including when we pseudo know that feature are related (just added feature)
713  //
714  // See https://github.com/qgis/QGIS/pull/45703
715  //
716  if ( mNmRelation.isValid() )
717  {
718  QgsFeatureIds::iterator iterator = featureIdsMixedValues.begin();
719  while ( iterator != featureIdsMixedValues.end() )
720  {
721  bool mixedValues = false;
722  for ( QTreeWidgetItem *parentTreeWidgetItem : parentTreeWidgetItems )
723  {
724  if ( ! multimapChildFeatures.values( parentTreeWidgetItem ).contains( *iterator ) )
725  {
726  mixedValues = true;
727  break;
728  }
729  }
730 
731  if ( !mixedValues )
732  {
733  iterator = featureIdsMixedValues.erase( iterator );
734  continue;
735  }
736 
737  ++iterator;
738  }
739  }
740 
741  // Set multiedit info label
742  if ( featureIdsMixedValues.isEmpty() )
743  {
744  QIcon icon = QgsApplication::getThemeIcon( QStringLiteral( "/multieditSameValues.svg" ) );
745  mMultiEditInfoLabel->setPixmap( icon.pixmap( mMultiEditInfoLabel->height(),
746  mMultiEditInfoLabel->height() ) );
747  mMultiEditInfoLabel->setToolTip( tr( "All features in selection have equal relations" ) );
748  }
749  else
750  {
751  QIcon icon = QgsApplication::getThemeIcon( QStringLiteral( "/multieditMixedValues.svg" ) );
752  mMultiEditInfoLabel->setPixmap( icon.pixmap( mMultiEditInfoLabel->height(),
753  mMultiEditInfoLabel->height() ) );
754  mMultiEditInfoLabel->setToolTip( tr( "Some features in selection have different relations" ) );
755 
756  // Set italic font for mixed values
757  QFont fontItalic = mMultiEditTreeWidget->font();
758  fontItalic.setItalic( true );
759  for ( QTreeWidgetItem *parentTreeWidgetItem : parentTreeWidgetItems )
760  {
761  for ( int childIndex = 0; childIndex < parentTreeWidgetItem->childCount(); ++childIndex )
762  {
763  QTreeWidgetItem *childItem = parentTreeWidgetItem->child( childIndex );
764  const QgsFeatureId featureIdCurrentItem = childItem->data( 0, static_cast<int>( MultiEditTreeWidgetRole::FeatureId ) ).toInt();
765  if ( featureIdsMixedValues.contains( featureIdCurrentItem ) )
766  childItem->setFont( 0, fontItalic );
767  }
768  }
769  }
770 }
771 
772 QTreeWidgetItem *QgsRelationEditorWidget::createMultiEditTreeWidgetItem( const QgsFeature &feature, QgsVectorLayer *layer, MultiEditFeatureType type )
773 {
774  QTreeWidgetItem *treeWidgetItem = new QTreeWidgetItem();
775  treeWidgetItem->setText( 0, QgsVectorLayerUtils::getFeatureDisplayString( layer, feature ) );
776  treeWidgetItem->setIcon( 0, QgsIconUtils::iconForLayer( layer ) );
777  treeWidgetItem->setData( 0, static_cast<int>( MultiEditTreeWidgetRole::FeatureType ), static_cast<int>( type ) );
778  treeWidgetItem->setData( 0, static_cast<int>( MultiEditTreeWidgetRole::FeatureId ), feature.id() );
779  return treeWidgetItem;
780 }
781 
782 void QgsRelationEditorWidget::onKeyPressed( QKeyEvent *e )
783 {
784  if ( e->key() == Qt::Key_Escape )
785  {
786  window()->setVisible( true );
787  window()->raise();
788  window()->activateWindow();
789  unsetMapTool();
790  }
791 }
792 
793 void QgsRelationEditorWidget::mapToolDeactivated()
794 {
795  if ( mEditorContext.mainMessageBar() && mMessageBarItem )
796  {
797  mEditorContext.mainMessageBar()->popWidget( mMessageBarItem );
798  }
799  mMessageBarItem = nullptr;
800 }
801 
803 {
804  return QVariantMap( {{"buttons", qgsFlagValueToKeys( visibleButtons() )},
805  {"show_first_feature", mShowFirstFeature}} );
806 }
807 
808 void QgsRelationEditorWidget::setConfig( const QVariantMap &config )
809 {
810  mButtonsVisibility = qgsFlagKeysToValue( config.value( QStringLiteral( "buttons" ) ).toString(), QgsRelationEditorWidget::Button::AllButtons );
811  mShowFirstFeature = config.value( QStringLiteral( "show_first_feature" ), true ).toBool();
812  updateButtons();
813 }
814 
815 void QgsRelationEditorWidget::beforeSetRelationFeature( const QgsRelation &newRelation, const QgsFeature &newFeature )
816 {
817  Q_UNUSED( newRelation );
818  Q_UNUSED( newFeature );
819 
820  if ( ! mRelation.isValid() )
821  return;
822 
823  disconnect( mRelation.referencingLayer(), &QgsVectorLayer::editingStarted, this, &QgsRelationEditorWidget::updateButtons );
824  disconnect( mRelation.referencingLayer(), &QgsVectorLayer::editingStopped, this, &QgsRelationEditorWidget::updateButtons );
825 }
826 
828 {
829  if ( ! mRelation.isValid()
830  || mFeatureList.isEmpty() )
831  {
832  updateButtons();
833  return;
834  }
835 
836  connect( mRelation.referencingLayer(), &QgsVectorLayer::editingStarted, this, &QgsRelationEditorWidget::updateButtons );
837  connect( mRelation.referencingLayer(), &QgsVectorLayer::editingStopped, this, &QgsRelationEditorWidget::updateButtons );
838 
839  updateButtons();
840 
842  initDualView( mRelation.referencingLayer(), myRequest );
843 }
844 
845 void QgsRelationEditorWidget::beforeSetRelations( const QgsRelation &newRelation, const QgsRelation &newNmRelation )
846 {
847  Q_UNUSED( newRelation );
848  Q_UNUSED( newNmRelation );
849 
850  if ( mRelation.isValid() )
851  {
852  disconnect( mRelation.referencingLayer(), &QgsVectorLayer::editingStarted, this, &QgsRelationEditorWidget::updateButtons );
853  disconnect( mRelation.referencingLayer(), &QgsVectorLayer::editingStopped, this, &QgsRelationEditorWidget::updateButtons );
854  }
855 
856  if ( mNmRelation.isValid() )
857  {
858  disconnect( mNmRelation.referencedLayer(), &QgsVectorLayer::editingStarted, this, &QgsRelationEditorWidget::updateButtons );
859  disconnect( mNmRelation.referencedLayer(), &QgsVectorLayer::editingStopped, this, &QgsRelationEditorWidget::updateButtons );
860  }
861 }
862 
864 {
865  if ( !mRelation.isValid() )
866  return;
867 
868  connect( mRelation.referencingLayer(), &QgsVectorLayer::editingStarted, this, &QgsRelationEditorWidget::updateButtons );
869  connect( mRelation.referencingLayer(), &QgsVectorLayer::editingStopped, this, &QgsRelationEditorWidget::updateButtons );
870 
871  if ( mNmRelation.isValid() )
872  {
873  connect( mNmRelation.referencedLayer(), &QgsVectorLayer::editingStarted, this, &QgsRelationEditorWidget::updateButtons );
874  connect( mNmRelation.referencedLayer(), &QgsVectorLayer::editingStopped, this, &QgsRelationEditorWidget::updateButtons );
875  }
876 
877  updateButtons();
878 }
879 
881 {
882  return mFeatureSelectionMgr;
883 }
884 
886 {
887  const QgsFeatureIds selectedFids = selectedChildFeatureIds();
888  unlinkFeatures( selectedFids );
889 }
890 
892 {
893  duplicateFeatures( mFeatureSelectionMgr->selectedFeatureIds() );
894 }
895 
897 {
898  duplicateFeatures( mFeatureSelectionMgr->selectedFeatureIds() );
899 }
900 
902 {
903  const QgsFeatureIds selectedFids = selectedChildFeatureIds();
904  deleteFeatures( selectedFids );
905 }
906 
908 {
910  if ( !c )
911  return;
912 
913  c->zoomToFeatureIds(
915  mFeatureSelectionMgr->selectedFeatureIds()
916  );
917 }
918 
920 
921 
923  : QgsAbstractRelationEditorConfigWidget( relation, parent )
924 {
925  setupUi( this );
926 }
927 
929 {
930  QgsRelationEditorWidget::Buttons buttons;
931  buttons.setFlag( QgsRelationEditorWidget::Button::Link, mRelationShowLinkCheckBox->isChecked() );
932  buttons.setFlag( QgsRelationEditorWidget::Button::Unlink, mRelationShowUnlinkCheckBox->isChecked() );
933  buttons.setFlag( QgsRelationEditorWidget::Button::AddChildFeature, mRelationShowAddChildCheckBox->isChecked() );
934  buttons.setFlag( QgsRelationEditorWidget::Button::DuplicateChildFeature, mRelationShowDuplicateChildFeatureCheckBox->isChecked() );
935  buttons.setFlag( QgsRelationEditorWidget::Button::ZoomToChildFeature, mRelationShowZoomToFeatureCheckBox->isChecked() );
936  buttons.setFlag( QgsRelationEditorWidget::Button::DeleteChildFeature, mRelationDeleteChildFeatureCheckBox->isChecked() );
937  buttons.setFlag( QgsRelationEditorWidget::Button::SaveChildEdits, mRelationShowSaveChildEditsCheckBox->isChecked() );
938 
939  return QVariantMap(
940  {
941  {"buttons", qgsFlagValueToKeys( buttons )},
942  {"show_first_feature", mShowFirstFeature->isChecked()}
943  } );
944 }
945 
946 void QgsRelationEditorConfigWidget::setConfig( const QVariantMap &config )
947 {
948  const QgsRelationEditorWidget::Buttons buttons = qgsFlagKeysToValue( config.value( QStringLiteral( "buttons" ) ).toString(), QgsRelationEditorWidget::Button::AllButtons );
949 
950  mRelationShowLinkCheckBox->setChecked( buttons.testFlag( QgsRelationEditorWidget::Button::Link ) );
951  mRelationShowUnlinkCheckBox->setChecked( buttons.testFlag( QgsRelationEditorWidget::Button::Unlink ) );
952  mRelationShowAddChildCheckBox->setChecked( buttons.testFlag( QgsRelationEditorWidget::Button::AddChildFeature ) );
953  mRelationShowDuplicateChildFeatureCheckBox->setChecked( buttons.testFlag( QgsRelationEditorWidget::Button::DuplicateChildFeature ) );
954  mRelationShowZoomToFeatureCheckBox->setChecked( buttons.testFlag( QgsRelationEditorWidget::Button::ZoomToChildFeature ) );
955  mRelationDeleteChildFeatureCheckBox->setChecked( buttons.testFlag( QgsRelationEditorWidget::Button::DeleteChildFeature ) );
956  mRelationShowSaveChildEditsCheckBox->setChecked( buttons.testFlag( QgsRelationEditorWidget::Button::SaveChildEdits ) );
957  mShowFirstFeature->setChecked( config.value( QStringLiteral( "show_first_feature" ), true ).toBool() );
958 }
959 
960 
962 
963 
964 #ifndef SIP_RUN
966 {
967 
968 }
969 
971 {
972  return QStringLiteral( "relation_editor" );
973 }
974 
976 {
977  return QObject::tr( "Relation Editor" );
978 }
979 
980 QgsAbstractRelationEditorWidget *QgsRelationEditorWidgetFactory::create( const QVariantMap &config, QWidget *parent ) const
981 {
982  return new QgsRelationEditorWidget( config, parent );
983 }
984 
986 {
987  return static_cast<QgsAbstractRelationEditorConfigWidget *>( new QgsRelationEditorConfigWidget( relation, parent ) );
988 }
989 #endif
void reset(T *p=nullptr)
Will reset the managed pointer to p.
This class should be subclassed for every configurable relation widget type.
Base class to build new relation widgets.
void toggleEditing(bool state)
Toggles editing state of the widget.
QgsFeatureIds addFeature(const QgsGeometry &geometry=QgsGeometry())
Adds new features with given geometry Returns the Id of added features.
void deleteFeatures(const QgsFeatureIds &fids)
Deletes the features with fids.
void linkFeature()
Links a new feature to the relation.
QgsRelation relation() const
Returns the relation.
void unlinkFeatures(const QgsFeatureIds &fids)
Unlinks the features with fids.
void deleteFeature(QgsFeatureId fid=QgsFeatureId())
Delete a feature with given fid.
QgsFeature feature() const
Returns the widget's current feature If the widget is in multiedit mode only the first is returned.
bool multiEditModeActive() const
Returns true if editing multiple features at a time.
void saveEdits()
Saves the current modifications in the relation.
void unlinkFeature(QgsFeatureId fid=QgsFeatureId())
Unlinks a feature with given fid.
void duplicateFeatures(const QgsFeatureIds &fids)
Duplicates features.
This class is a menu that is populated automatically with the actions defined for a given layer.
Definition: qgsactionmenu.h:38
static QIcon getThemeIcon(const QString &name, const QColor &fillColor=QColor(), const QColor &strokeColor=QColor())
Helper to get a theme icon.
This class contains context information for attribute editor widgets.
QgsMapCanvas * mapCanvas() const
Returns the associated map canvas (e.g.
QgsMessageBar * mainMessageBar()
Returns the main message bar.
QgsAdvancedDigitizingDockWidget * cadDockWidget() const
Returns the associated CAD dock widget (e.g.
void setParentFormFeature(const QgsFeature &feature)
Sets the feature of the currently edited parent form.
This widget is used to show the attributes of a set of features of a QgsVectorLayer.
Definition: qgsdualview.h:45
void showContextMenuExternally(QgsActionMenu *menu, QgsFeatureId fid)
Emitted when selecting context menu on the feature list to create the context menu individually.
ViewMode
The view modes, in which this widget can present information.
Definition: qgsdualview.h:56
@ AttributeTable
Shows the features and attributes in a table layout.
Definition: qgsdualview.h:61
@ AttributeEditor
Show a list of the features, where one can be chosen and the according attribute dialog will be prese...
Definition: qgsdualview.h:68
void setFeatureSelectionManager(QgsIFeatureSelectionManager *featureSelectionManager)
Set the feature selection model.
void init(QgsVectorLayer *layer, QgsMapCanvas *mapCanvas, const QgsFeatureRequest &request=QgsFeatureRequest(), const QgsAttributeEditorContext &context=QgsAttributeEditorContext(), bool loadFeatures=true, bool showFirstFeature=true)
Has to be called to initialize the dual view.
void parentFormValueChanged(const QString &attribute, const QVariant &value)
Called in embedded forms when an attribute value in the parent form has changed.
void setView(ViewMode view)
Change the current view mode.
QString expression() const
Returns the original, unmodified expression string.
Wrapper for iterator of features from vector data provider or vector layer.
bool nextFeature(QgsFeature &f)
This class wraps a request for features to a vector layer (or directly its vector data provider).
QgsExpression * filterExpression() const
Returns the filter expression (if set).
QgsFeatureRequest & setFilterExpression(const QString &expression)
Set the filter expression.
The feature class encapsulates a single feature including its unique ID, geometry and a list of field...
Definition: qgsfeature.h:56
QgsGeometry geometry
Definition: qgsfeature.h:67
Q_GADGET QgsFeatureId id
Definition: qgsfeature.h:64
Is an interface class to abstract feature selection handling.
void selectionChanged(const QgsFeatureIds &selected, const QgsFeatureIds &deselected, bool clearAndSelect)
Emitted when selection was changed.
static QIcon iconForLayer(const QgsMapLayer *layer)
Returns the icon corresponding to a specified map layer.
static void warning(const QString &msg)
Goes to qWarning.
Definition: qgslogger.cpp:122
Map canvas is a class for displaying all GIS data types on a canvas.
Definition: qgsmapcanvas.h:90
void unsetMapTool(QgsMapTool *mapTool)
Unset the current map tool or last non zoom tool.
void keyPressed(QKeyEvent *e)
Emit key press event.
void setMapTool(QgsMapTool *mapTool, bool clean=false)
Sets the map tool currently being used on the canvas.
QString name
Definition: qgsmaplayer.h:76
void editingStopped()
Emitted when edited changes have been successfully written to the data provider.
void editingStarted()
Emitted when editing on this layer has started.
This tool digitizes geometry of new point/line/polygon features on already existing vector layers Onc...
void digitizingCompleted(const QgsFeature &feature)
Emitted whenever the digitizing has been successfully completed.
void setLayer(QgsMapLayer *vl)
Change the layer edited by the map tool.
Abstract base class for all map tools.
Definition: qgsmaptool.h:71
void deactivated()
signal emitted once the map tool is deactivated
void setButton(QAbstractButton *button)
Use this to associate a button to this maptool.
Definition: qgsmaptool.cpp:150
bool popWidget(QgsMessageBarItem *item)
Remove the specified item from the bar, and display the next most recent one in the stack.
static QgsMessageBarItem * createMessage(const QString &text, QWidget *parent=nullptr)
Creates message bar item widget containing a message text to be displayed on the bar.
Creates a new configuration widget for the relation editor widget.
void setConfig(const QVariantMap &config)
Update the configuration widget to represent the given configuration.
QgsRelationEditorConfigWidget(const QgsRelation &relation, QWidget *parent)
Create a new configuration widget.
QVariantMap config()
Create a configuration from the current GUI state.
QString type() const override
Returns the machine readable identifier name of this widget type.
QString name() const override
Returns the human readable identifier name of this widget type.
QgsAbstractRelationEditorConfigWidget * configWidget(const QgsRelation &relation, QWidget *parent) const override
Override this in your implementation.
QgsAbstractRelationEditorWidget * create(const QVariantMap &config, QWidget *parent=nullptr) const override
Override this in your implementation.
The default relation widget in QGIS.
Q_DECL_DEPRECATED void duplicateFeature()
Duplicates a feature.
void zoomToSelectedFeatures()
Zooms to the selected features.
Button
Possible buttons shown in the relation editor.
void beforeSetRelationFeature(const QgsRelation &newRelation, const QgsFeature &newFeature) override
A hook called right before setRelationFeature() is executed.
void parentFormValueChanged(const QString &attribute, const QVariant &newValue) override
void setEditorContext(const QgsAttributeEditorContext &context) override
Sets the editor context.
QgsIFeatureSelectionManager * featureSelectionManager()
The feature selection manager is responsible for the selected features which are currently being edit...
virtual void updateUi() override
A hook called every time the state of the relation editor widget has changed via calling its set* met...
void unlinkSelectedFeatures()
Unlinks the selected features from the relation.
void setViewMode(QgsDualView::ViewMode mode)
Define the view mode for the dual view.
void setVisibleButtons(const Buttons &buttons)
Defines the buttons which are shown.
void duplicateSelectedFeatures()
Duplicates the selected features.
void afterSetRelationFeature() override
A hook called right after setRelationFeature() is executed, but before updateUi() is called.
QgsRelationEditorWidget(const QVariantMap &config, QWidget *parent=nullptr)
Constructor.
QVariantMap config() const override
Returns the current configuration.
void setConfig(const QVariantMap &config) override
Defines the current configuration.
void beforeSetRelations(const QgsRelation &newRelation, const QgsRelation &newNmRelation) override
A hook called right before setRelations() is executed.
void afterSetRelations() override
A hook called right after setRelations() is executed, but before updateUi() is called.
void deleteSelectedFeatures()
Deletes the currently selected features.
QgsFeatureRequest getReferencedFeatureRequest(const QgsAttributes &attributes) const
Creates a request to return the feature on the referenced (parent) layer which is referenced by the p...
QgsVectorLayer * referencedLayer
Definition: qgsrelation.h:48
QgsVectorLayer * referencingLayer
Definition: qgsrelation.h:47
bool isValid
Definition: qgsrelation.h:50
QgsFeatureRequest getRelatedFeaturesRequest(const QgsFeature &feature) const
Creates a request to return all the features on the referencing (child) layer which have a foreign ke...
int selectedFeatureCount() override
Returns the number of features that are selected in this layer.
const QgsFeatureIds & selectedFeatureIds() const override
Returns reference to identifiers of selected features.
static QString getFeatureDisplayString(const QgsVectorLayer *layer, const QgsFeature &feature)
Represents a vector layer which manages a vector based data sets.
Q_INVOKABLE QgsWkbTypes::GeometryType geometryType() const
Returns point, line or polygon.
bool isSpatial() const FINAL
Returns true if this is a geometry layer and false in case of NoGeometry (table only) or UnknownGeome...
QgsFeatureIterator getFeatures(const QgsFeatureRequest &request=QgsFeatureRequest()) const FINAL
Queries the layer for features specified in request.
Q_INVOKABLE const QgsFeatureIds & selectedFeatureIds() const
Returns a list of the selected features IDs in this layer.
bool isEditable() const FINAL
Returns true if the provider is in editing mode.
QgsFeature getFeature(QgsFeatureId fid) const
Queries the layer for the feature with the given id.
void selectionChanged(const QgsFeatureIds &selected, const QgsFeatureIds &deselected, bool clearAndSelect)
Emitted when selection was changed.
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
QString qgsFlagValueToKeys(const T &value, bool *returnOk=nullptr)
Returns the value for the given keys of a flag.
Definition: qgis.h:2211
T qgsFlagKeysToValue(const QString &keys, const T &defaultValue, bool tryValueAsKey=true, bool *returnOk=nullptr)
Returns the value corresponding to the given keys of a flag.
Definition: qgis.h:2233
QSet< QgsFeatureId > QgsFeatureIds
Definition: qgsfeatureid.h:37
qint64 QgsFeatureId
64 bit feature ids negative numbers are used for uncommitted/newly added features
Definition: qgsfeatureid.h:28