QGIS API Documentation  3.22.4-Białowieża (ce8e65e95e)
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 "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 #include "qgscollapsiblegroupbox.h"
39 
40 #include <QHBoxLayout>
41 #include <QLabel>
42 #include <QMessageBox>
43 #include <QPushButton>
44 
47 QgsFilteredSelectionManager::QgsFilteredSelectionManager( QgsVectorLayer *layer, const QgsFeatureRequest &request, QObject *parent )
48  : QgsVectorLayerSelectionManager( layer, parent )
49  , mRequest( request )
50 {
51  if ( ! layer )
52  return;
53 
54  for ( const auto fid : layer->selectedFeatureIds() )
55  if ( mRequest.acceptFeature( layer->getFeature( fid ) ) )
56  mSelectedFeatureIds << fid;
57 
58  connect( layer, &QgsVectorLayer::selectionChanged, this, &QgsFilteredSelectionManager::onSelectionChanged );
59 }
60 
61 const QgsFeatureIds &QgsFilteredSelectionManager::selectedFeatureIds() const
62 
63 
64 {
65  return mSelectedFeatureIds;
66 }
67 
68 int QgsFilteredSelectionManager::selectedFeatureCount()
69 {
70  return mSelectedFeatureIds.count();
71 }
72 
73 void QgsFilteredSelectionManager::onSelectionChanged( const QgsFeatureIds &selected, const QgsFeatureIds &deselected, bool clearAndSelect )
74 {
75  QgsFeatureIds lselected = selected;
76  if ( clearAndSelect )
77  {
78  mSelectedFeatureIds.clear();
79  }
80  else
81  {
82  for ( const auto fid : deselected )
83  mSelectedFeatureIds.remove( fid );
84  }
85 
86  for ( const auto fid : selected )
87  if ( mRequest.acceptFeature( layer()->getFeature( fid ) ) )
88  mSelectedFeatureIds << fid;
89  else
90  lselected.remove( fid );
91 
92  emit selectionChanged( lselected, deselected, clearAndSelect );
93 }
94 
96 
97 QgsRelationEditorWidget::QgsRelationEditorWidget( const QVariantMap &config, QWidget *parent )
98  : QgsAbstractRelationEditorWidget( config, parent )
99  , mButtonsVisibility( qgsFlagKeysToValue( config.value( QStringLiteral( "buttons" ) ).toString(), QgsRelationEditorWidget::Button::AllButtons ) )
100  , mShowFirstFeature( config.value( QStringLiteral( "show_first_feature" ), true ).toBool() )
101 {
102  QVBoxLayout *rootLayout = new QVBoxLayout( this );
103  rootLayout->setContentsMargins( 0, 9, 0, 0 );
104 
105  // buttons
106  QHBoxLayout *buttonLayout = new QHBoxLayout();
107  buttonLayout->setContentsMargins( 0, 0, 0, 0 );
108  // toggle editing
109  mToggleEditingButton = new QToolButton( this );
110  mToggleEditingButton->setObjectName( QStringLiteral( "mToggleEditingButton" ) );
111  mToggleEditingButton->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mActionToggleEditing.svg" ) ) );
112  mToggleEditingButton->setText( tr( "Toggle Editing" ) );
113  mToggleEditingButton->setEnabled( false );
114  mToggleEditingButton->setCheckable( true );
115  mToggleEditingButton->setToolTip( tr( "Toggle editing mode for child layer" ) );
116  buttonLayout->addWidget( mToggleEditingButton );
117  // save Edits
118  mSaveEditsButton = new QToolButton( this );
119  mSaveEditsButton->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mActionSaveEdits.svg" ) ) );
120  mSaveEditsButton->setText( tr( "Save Child Layer Edits" ) );
121  mSaveEditsButton->setToolTip( tr( "Save child layer edits" ) );
122  mSaveEditsButton->setEnabled( true );
123  buttonLayout->addWidget( mSaveEditsButton );
124  // add feature with geometry
125  mAddFeatureGeometryButton = new QToolButton( this );
126  mAddFeatureGeometryButton->setObjectName( QStringLiteral( "mAddFeatureGeometryButton" ) );
127  buttonLayout->addWidget( mAddFeatureGeometryButton );
128  // add feature
129  mAddFeatureButton = new QToolButton( this );
130  mAddFeatureButton->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mActionNewTableRow.svg" ) ) );
131  mAddFeatureButton->setText( tr( "Add Child Feature" ) );
132  mAddFeatureButton->setToolTip( tr( "Add child feature" ) );
133  mAddFeatureButton->setObjectName( QStringLiteral( "mAddFeatureButton" ) );
134  buttonLayout->addWidget( mAddFeatureButton );
135  // duplicate feature
136  mDuplicateFeatureButton = new QToolButton( this );
137  mDuplicateFeatureButton->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mActionDuplicateFeature.svg" ) ) );
138  mDuplicateFeatureButton->setText( tr( "Duplicate Child Feature" ) );
139  mDuplicateFeatureButton->setToolTip( tr( "Duplicate selected child feature" ) );
140  mDuplicateFeatureButton->setObjectName( QStringLiteral( "mDuplicateFeatureButton" ) );
141  buttonLayout->addWidget( mDuplicateFeatureButton );
142  // delete feature
143  mDeleteFeatureButton = new QToolButton( this );
144  mDeleteFeatureButton->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mActionDeleteSelected.svg" ) ) );
145  mDeleteFeatureButton->setText( tr( "Delete Child Feature" ) );
146  mDeleteFeatureButton->setToolTip( tr( "Delete selected child feature" ) );
147  mDeleteFeatureButton->setObjectName( QStringLiteral( "mDeleteFeatureButton" ) );
148  buttonLayout->addWidget( mDeleteFeatureButton );
149  // link feature
150  mLinkFeatureButton = new QToolButton( this );
151  mLinkFeatureButton->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mActionLink.svg" ) ) );
152  mLinkFeatureButton->setText( tr( "Link Existing Features" ) );
153  mLinkFeatureButton->setToolTip( tr( "Link existing child features" ) );
154  mLinkFeatureButton->setObjectName( QStringLiteral( "mLinkFeatureButton" ) );
155  buttonLayout->addWidget( mLinkFeatureButton );
156  // unlink feature
157  mUnlinkFeatureButton = new QToolButton( this );
158  mUnlinkFeatureButton->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mActionUnlink.svg" ) ) );
159  mUnlinkFeatureButton->setText( tr( "Unlink Feature" ) );
160  mUnlinkFeatureButton->setToolTip( tr( "Unlink selected child feature" ) );
161  mUnlinkFeatureButton->setObjectName( QStringLiteral( "mUnlinkFeatureButton" ) );
162  buttonLayout->addWidget( mUnlinkFeatureButton );
163  // zoom to linked feature
164  mZoomToFeatureButton = new QToolButton( this );
165  mZoomToFeatureButton->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mActionZoomToSelected.svg" ) ) );
166  mZoomToFeatureButton->setText( tr( "Zoom To Feature" ) );
167  mZoomToFeatureButton->setToolTip( tr( "Zoom to selected child feature" ) );
168  mZoomToFeatureButton->setObjectName( QStringLiteral( "mZoomToFeatureButton" ) );
169  buttonLayout->addWidget( mZoomToFeatureButton );
170  // spacer
171  buttonLayout->addItem( new QSpacerItem( 0, 0, QSizePolicy::Expanding ) );
172  // form view
173  mFormViewButton = new QToolButton( this );
174  mFormViewButton->setText( tr( "Form View" ) );
175  mFormViewButton->setToolTip( tr( "Switch to form view" ) );
176  mFormViewButton->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mActionPropertyItem.svg" ) ) );
177  mFormViewButton->setCheckable( true );
178  mFormViewButton->setChecked( mViewMode == QgsDualView::AttributeEditor );
179  buttonLayout->addWidget( mFormViewButton );
180  // table view
181  mTableViewButton = new QToolButton( this );
182  mTableViewButton->setText( tr( "Table View" ) );
183  mTableViewButton->setToolTip( tr( "Switch to table view" ) );
184  mTableViewButton->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mActionOpenTable.svg" ) ) );
185  mTableViewButton->setCheckable( true );
186  mTableViewButton->setChecked( mViewMode == QgsDualView::AttributeTable );
187  buttonLayout->addWidget( mTableViewButton );
188  // button group
189  mViewModeButtonGroup = new QButtonGroup( this );
190  mViewModeButtonGroup->addButton( mFormViewButton, QgsDualView::AttributeEditor );
191  mViewModeButtonGroup->addButton( mTableViewButton, QgsDualView::AttributeTable );
192 
193  // add buttons layout
194  rootLayout->addLayout( buttonLayout );
195 
196  // add dual view
197  QGridLayout *relationLayout = new QGridLayout();
198  relationLayout->setContentsMargins( 0, 0, 0, 0 );
199  mDualView = new QgsDualView( this );
200  mDualView->setView( mViewMode );
201  connect( mDualView, &QgsDualView::showContextMenuExternally, this, &QgsRelationEditorWidget::showContextMenu );
202  relationLayout->addWidget( mDualView );
203  rootLayout->addLayout( relationLayout );
204 
205 #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
206  connect( mViewModeButtonGroup, static_cast<void ( QButtonGroup::* )( int )>( &QButtonGroup::buttonClicked ),
207  this, static_cast<void ( QgsRelationEditorWidget::* )( int )>( &QgsRelationEditorWidget::setViewMode ) );
208 #else
209  connect( mViewModeButtonGroup, &QButtonGroup::idClicked,
210  this, static_cast<void ( QgsRelationEditorWidget::* )( int )>( &QgsRelationEditorWidget::setViewMode ) );
211 #endif
212  connect( mToggleEditingButton, &QAbstractButton::clicked, this, &QgsRelationEditorWidget::toggleEditing );
213  connect( mSaveEditsButton, &QAbstractButton::clicked, this, &QgsRelationEditorWidget::saveEdits );
214  connect( mAddFeatureButton, &QAbstractButton::clicked, this, [this]() { addFeature(); } );
215  connect( mAddFeatureGeometryButton, &QAbstractButton::clicked, this, &QgsRelationEditorWidget::addFeatureGeometry );
216  connect( mDuplicateFeatureButton, &QAbstractButton::clicked, this, &QgsRelationEditorWidget::duplicateSelectedFeatures );
217  connect( mDeleteFeatureButton, &QAbstractButton::clicked, this, &QgsRelationEditorWidget::deleteSelectedFeatures );
218  connect( mLinkFeatureButton, &QAbstractButton::clicked, this, &QgsRelationEditorWidget::linkFeature );
219  connect( mUnlinkFeatureButton, &QAbstractButton::clicked, this, &QgsRelationEditorWidget::unlinkSelectedFeatures );
220  connect( mZoomToFeatureButton, &QAbstractButton::clicked, this, &QgsRelationEditorWidget::zoomToSelectedFeatures );
221 
222  // Set initial state for add/remove etc. buttons
223  updateButtons();
224 
225  setLayout( rootLayout );
226 }
227 
228 void QgsRelationEditorWidget::initDualView( QgsVectorLayer *layer, const QgsFeatureRequest &request )
229 {
232  mDualView->init( layer, mEditorContext.mapCanvas(), request, ctx, true, mShowFirstFeature );
233  mFeatureSelectionMgr = new QgsFilteredSelectionManager( layer, request, mDualView );
234  mDualView->setFeatureSelectionManager( mFeatureSelectionMgr );
235 
236  connect( mFeatureSelectionMgr, &QgsIFeatureSelectionManager::selectionChanged, this, &QgsRelationEditorWidget::updateButtons );
237 
238  QIcon icon;
239  QString text;
240  if ( layer->geometryType() == QgsWkbTypes::PointGeometry )
241  {
242  icon = QgsApplication::getThemeIcon( QStringLiteral( "/mActionCapturePoint.svg" ) );
243  text = tr( "Add Point Child Feature" );
244  }
245  else if ( layer->geometryType() == QgsWkbTypes::LineGeometry )
246  {
247  icon = QgsApplication::getThemeIcon( QStringLiteral( "/mActionCaptureLine.svg" ) );
248  text = tr( "Add Line Child Feature" );
249  }
250  else if ( layer->geometryType() == QgsWkbTypes::PolygonGeometry )
251  {
252  icon = QgsApplication::getThemeIcon( QStringLiteral( "/mActionCapturePolygon.svg" ) );
253  text = tr( "Add Polygon Child Feature" );
254  }
255 
256  mAddFeatureGeometryButton->setIcon( icon );
257  mAddFeatureGeometryButton->setText( text );
258  mAddFeatureGeometryButton->setToolTip( text );
259 
260  updateButtons();
261 }
262 
264 {
265  mEditorContext = context;
266 
267  if ( context.mapCanvas() && context.cadDockWidget() )
268  {
269  mMapToolDigitize.reset( new QgsMapToolDigitizeFeature( context.mapCanvas(), context.cadDockWidget() ) );
270  mMapToolDigitize->setButton( mAddFeatureGeometryButton );
271  }
272 
273  updateButtons();
274 }
275 
277 {
278  mDualView->setView( mode );
279  mViewMode = mode;
280 }
281 
282 void QgsRelationEditorWidget::updateButtons()
283 {
284  bool toggleEditingButtonEnabled = false;
285  bool editable = false;
286  bool linkable = false;
287  bool spatial = false;
288  const bool selectionNotEmpty = mFeatureSelectionMgr ? mFeatureSelectionMgr->selectedFeatureCount() : false;
289 
290  if ( mRelation.isValid() )
291  {
292  toggleEditingButtonEnabled = mRelation.referencingLayer()->supportsEditing();
293  editable = mRelation.referencingLayer()->isEditable();
294  linkable = mRelation.referencingLayer()->isEditable();
295  spatial = mRelation.referencingLayer()->isSpatial();
296  }
297 
298  if ( mNmRelation.isValid() )
299  {
300  toggleEditingButtonEnabled |= mNmRelation.referencedLayer()->supportsEditing();
301  editable = mNmRelation.referencedLayer()->isEditable();
302  spatial = mNmRelation.referencedLayer()->isSpatial();
303  }
304 
305  mToggleEditingButton->setEnabled( toggleEditingButtonEnabled );
306  mAddFeatureButton->setEnabled( editable );
307  mAddFeatureGeometryButton->setEnabled( editable );
308  mDuplicateFeatureButton->setEnabled( editable && selectionNotEmpty );
309  mLinkFeatureButton->setEnabled( linkable );
310  mDeleteFeatureButton->setEnabled( editable && selectionNotEmpty );
311  mUnlinkFeatureButton->setEnabled( linkable && selectionNotEmpty );
312  mZoomToFeatureButton->setEnabled( selectionNotEmpty );
313  mToggleEditingButton->setChecked( editable );
314  mSaveEditsButton->setEnabled( editable || linkable );
315 
316  mToggleEditingButton->setVisible( !mLayerInSameTransactionGroup );
317 
318  mLinkFeatureButton->setVisible( mButtonsVisibility.testFlag( QgsRelationEditorWidget::Button::Link ) );
319  mUnlinkFeatureButton->setVisible( mButtonsVisibility.testFlag( QgsRelationEditorWidget::Button::Unlink ) );
320  mSaveEditsButton->setVisible( mButtonsVisibility.testFlag( QgsRelationEditorWidget::Button::SaveChildEdits ) && !mLayerInSameTransactionGroup );
321  mAddFeatureButton->setVisible( mButtonsVisibility.testFlag( QgsRelationEditorWidget::Button::AddChildFeature ) );
322  mAddFeatureGeometryButton->setVisible( mButtonsVisibility.testFlag( QgsRelationEditorWidget::Button::AddChildFeature ) && mEditorContext.mapCanvas() && mEditorContext.cadDockWidget() && spatial );
323  mDuplicateFeatureButton->setVisible( mButtonsVisibility.testFlag( QgsRelationEditorWidget::Button::DuplicateChildFeature ) );
324  mDeleteFeatureButton->setVisible( mButtonsVisibility.testFlag( QgsRelationEditorWidget::Button::DeleteChildFeature ) );
325  mZoomToFeatureButton->setVisible( mButtonsVisibility.testFlag( QgsRelationEditorWidget::Button::ZoomToChildFeature ) && mEditorContext.mapCanvas() && spatial );
326 }
327 
328 void QgsRelationEditorWidget::addFeatureGeometry()
329 {
330  QgsVectorLayer *layer = nullptr;
331  if ( mNmRelation.isValid() )
332  layer = mNmRelation.referencedLayer();
333  else
334  layer = mRelation.referencingLayer();
335 
336  mMapToolDigitize->setLayer( layer );
337 
338  // window is always on top, so we hide it to digitize without seeing it
339  window()->setVisible( false );
340  setMapTool( mMapToolDigitize );
341 
342  connect( mMapToolDigitize, &QgsMapToolDigitizeFeature::digitizingCompleted, this, &QgsRelationEditorWidget::onDigitizingCompleted );
343  connect( mEditorContext.mapCanvas(), &QgsMapCanvas::keyPressed, this, &QgsRelationEditorWidget::onKeyPressed );
344 
345  if ( auto *lMainMessageBar = mEditorContext.mainMessageBar() )
346  {
347  const QString displayString = QgsVectorLayerUtils::getFeatureDisplayString( layer, mFeature );
348 
349  const QString title = tr( "Create child feature for parent %1 \"%2\"" ).arg( mRelation.referencedLayer()->name(), displayString );
350  const QString msg = tr( "Digitize the geometry for the new feature on layer %1. Press &lt;ESC&gt; to cancel." )
351  .arg( layer->name() );
352  mMessageBarItem = QgsMessageBar::createMessage( title, msg, this );
353  lMainMessageBar->pushItem( mMessageBarItem );
354  }
355 
356 }
357 
358 void QgsRelationEditorWidget::onDigitizingCompleted( const QgsFeature &feature )
359 {
361 
362  unsetMapTool();
363 }
364 
365 void QgsRelationEditorWidget::toggleEditing( bool state )
366 {
368 
369  updateButtons();
370 }
371 
373 {
374  if ( !mRelation.isValid() || !mFeature.isValid() )
375  return;
376 
377  if ( !isVisible() )
378  return;
379 
381 
382  if ( mNmRelation.isValid() )
383  {
385  QgsFeature fet;
386  QStringList filters;
387 
388  while ( it.nextFeature( fet ) )
389  {
391  filters << filter.prepend( '(' ).append( ')' );
392  }
393 
394  QgsFeatureRequest nmRequest;
395  nmRequest.setFilterExpression( filters.join( QLatin1String( " OR " ) ) );
396 
397  initDualView( mNmRelation.referencedLayer(), nmRequest );
398  }
399  else if ( mRelation.referencingLayer() )
400  {
401  initDualView( mRelation.referencingLayer(), request );
402  }
403 }
404 
405 void QgsRelationEditorWidget::setVisibleButtons( const Buttons &buttons )
406 {
407  mButtonsVisibility = buttons;
408  updateButtons();
409 }
410 
411 QgsRelationEditorWidget::Buttons QgsRelationEditorWidget::visibleButtons() const
412 {
413  Buttons buttons;
414  if ( mLinkFeatureButton->isVisible() )
415  buttons |= Button::Link;
416  if ( mUnlinkFeatureButton->isVisible() )
417  buttons |= Button::Unlink;
418  if ( mSaveEditsButton->isVisible() )
419  buttons |= Button::SaveChildEdits;
420  if ( mAddFeatureButton->isVisible() )
421  buttons |= Button::AddChildFeature;
422  if ( mDuplicateFeatureButton->isVisible() )
423  buttons |= Button::DuplicateChildFeature;
424  if ( mDeleteFeatureButton->isVisible() )
425  buttons |= Button::DeleteChildFeature;
426  if ( mZoomToFeatureButton->isVisible() )
427  buttons |= Button::ZoomToChildFeature;
428  return buttons;
429 }
430 
431 void QgsRelationEditorWidget::parentFormValueChanged( const QString &attribute, const QVariant &newValue )
432 {
433  mDualView->parentFormValueChanged( attribute, newValue );
434 }
435 
436 void QgsRelationEditorWidget::showContextMenu( QgsActionMenu *menu, const QgsFeatureId fid )
437 {
439  {
440  QAction *qAction = nullptr;
441 
442  qAction = menu->addAction( QgsApplication::getThemeIcon( QStringLiteral( "/mActionDeleteSelected.svg" ) ), tr( "Delete Feature" ) );
443  connect( qAction, &QAction::triggered, this, [this, fid]() { deleteFeature( fid ); } );
444 
445  qAction = menu->addAction( QgsApplication::getThemeIcon( QStringLiteral( "/mActionUnlink.svg" ) ), tr( "Unlink Feature" ) );
446  connect( qAction, &QAction::triggered, this, [this, fid]() { unlinkFeature( fid ); } );
447  }
448 }
449 
450 void QgsRelationEditorWidget::setMapTool( QgsMapTool *mapTool )
451 {
452  QgsMapCanvas *mapCanvas = mEditorContext.mapCanvas();
453 
454  mapCanvas->setMapTool( mapTool );
455  mapCanvas->window()->raise();
456  mapCanvas->activateWindow();
457  mapCanvas->setFocus();
458  connect( mapTool, &QgsMapTool::deactivated, this, &QgsRelationEditorWidget::mapToolDeactivated );
459 }
460 
461 void QgsRelationEditorWidget::unsetMapTool()
462 {
463  QgsMapCanvas *mapCanvas = mEditorContext.mapCanvas();
464 
465  // this will call mapToolDeactivated
466  mapCanvas->unsetMapTool( mMapToolDigitize );
467 
468  disconnect( mapCanvas, &QgsMapCanvas::keyPressed, this, &QgsRelationEditorWidget::onKeyPressed );
469  disconnect( mMapToolDigitize, &QgsMapToolDigitizeFeature::digitizingCompleted, this, &QgsRelationEditorWidget::onDigitizingCompleted );
470 }
471 
472 void QgsRelationEditorWidget::onKeyPressed( QKeyEvent *e )
473 {
474  if ( e->key() == Qt::Key_Escape )
475  {
476  unsetMapTool();
477  }
478 }
479 
480 void QgsRelationEditorWidget::mapToolDeactivated()
481 {
482  window()->setVisible( true );
483  window()->raise();
484  window()->activateWindow();
485 
486  if ( mEditorContext.mainMessageBar() && mMessageBarItem )
487  {
488  mEditorContext.mainMessageBar()->popWidget( mMessageBarItem );
489  }
490  mMessageBarItem = nullptr;
491 }
492 
494 {
495  return QVariantMap( {{"buttons", qgsFlagValueToKeys( visibleButtons() )},
496  {"show_first_feature", mShowFirstFeature}} );
497 }
498 
499 void QgsRelationEditorWidget::setConfig( const QVariantMap &config )
500 {
501  mButtonsVisibility = qgsFlagKeysToValue( config.value( QStringLiteral( "buttons" ) ).toString(), QgsRelationEditorWidget::Button::AllButtons );
502  mShowFirstFeature = config.value( QStringLiteral( "show_first_feature" ), true ).toBool();
503  updateButtons();
504 }
505 
506 void QgsRelationEditorWidget::beforeSetRelationFeature( const QgsRelation &newRelation, const QgsFeature &newFeature )
507 {
508  Q_UNUSED( newRelation );
509  Q_UNUSED( newFeature );
510 
511  if ( ! mRelation.isValid() )
512  return;
513 
514  disconnect( mRelation.referencingLayer(), &QgsVectorLayer::editingStarted, this, &QgsRelationEditorWidget::updateButtons );
515  disconnect( mRelation.referencingLayer(), &QgsVectorLayer::editingStopped, this, &QgsRelationEditorWidget::updateButtons );
516 }
517 
519 {
520  if ( ! mRelation.isValid() )
521  {
522  updateButtons();
523  return;
524  }
525 
526  connect( mRelation.referencingLayer(), &QgsVectorLayer::editingStarted, this, &QgsRelationEditorWidget::updateButtons );
527  connect( mRelation.referencingLayer(), &QgsVectorLayer::editingStopped, this, &QgsRelationEditorWidget::updateButtons );
528 
529  updateButtons();
530 
532  initDualView( mRelation.referencingLayer(), myRequest );
533 }
534 
535 void QgsRelationEditorWidget::beforeSetRelations( const QgsRelation &newRelation, const QgsRelation &newNmRelation )
536 {
537  Q_UNUSED( newRelation );
538  Q_UNUSED( newNmRelation );
539 
540  if ( mRelation.isValid() )
541  {
542  disconnect( mRelation.referencingLayer(), &QgsVectorLayer::editingStarted, this, &QgsRelationEditorWidget::updateButtons );
543  disconnect( mRelation.referencingLayer(), &QgsVectorLayer::editingStopped, this, &QgsRelationEditorWidget::updateButtons );
544  }
545 
546  if ( mNmRelation.isValid() )
547  {
548  disconnect( mNmRelation.referencedLayer(), &QgsVectorLayer::editingStarted, this, &QgsRelationEditorWidget::updateButtons );
549  disconnect( mNmRelation.referencedLayer(), &QgsVectorLayer::editingStopped, this, &QgsRelationEditorWidget::updateButtons );
550  }
551 }
552 
554 {
555  if ( !mRelation.isValid() )
556  return;
557 
558  connect( mRelation.referencingLayer(), &QgsVectorLayer::editingStarted, this, &QgsRelationEditorWidget::updateButtons );
559  connect( mRelation.referencingLayer(), &QgsVectorLayer::editingStopped, this, &QgsRelationEditorWidget::updateButtons );
560 
561  if ( mNmRelation.isValid() )
562  {
563  connect( mNmRelation.referencedLayer(), &QgsVectorLayer::editingStarted, this, &QgsRelationEditorWidget::updateButtons );
564  connect( mNmRelation.referencedLayer(), &QgsVectorLayer::editingStopped, this, &QgsRelationEditorWidget::updateButtons );
565  }
566 
567  updateButtons();
568 }
569 
571 {
572  return mFeatureSelectionMgr;
573 }
574 
576 {
577  unlinkFeatures( mFeatureSelectionMgr->selectedFeatureIds() );
578 }
579 
581 {
582  duplicateFeatures( mFeatureSelectionMgr->selectedFeatureIds() );
583 }
584 
586 {
587  duplicateFeatures( mFeatureSelectionMgr->selectedFeatureIds() );
588 }
589 
591 {
592  const QgsFeatureIds selectedFids = mFeatureSelectionMgr->selectedFeatureIds();
593  deleteFeatures( selectedFids );
594 }
595 
597 {
599  if ( !c )
600  return;
601 
602  c->zoomToFeatureIds(
604  mFeatureSelectionMgr->selectedFeatureIds()
605  );
606 }
607 
609 
610 
612  : QgsAbstractRelationEditorConfigWidget( relation, parent )
613 {
614  setupUi( this );
615 }
616 
618 {
619  QgsRelationEditorWidget::Buttons buttons;
620  buttons.setFlag( QgsRelationEditorWidget::Button::Link, mRelationShowLinkCheckBox->isChecked() );
621  buttons.setFlag( QgsRelationEditorWidget::Button::Unlink, mRelationShowUnlinkCheckBox->isChecked() );
622  buttons.setFlag( QgsRelationEditorWidget::Button::AddChildFeature, mRelationShowAddChildCheckBox->isChecked() );
623  buttons.setFlag( QgsRelationEditorWidget::Button::DuplicateChildFeature, mRelationShowDuplicateChildFeatureCheckBox->isChecked() );
624  buttons.setFlag( QgsRelationEditorWidget::Button::ZoomToChildFeature, mRelationShowZoomToFeatureCheckBox->isChecked() );
625  buttons.setFlag( QgsRelationEditorWidget::Button::DeleteChildFeature, mRelationDeleteChildFeatureCheckBox->isChecked() );
626  buttons.setFlag( QgsRelationEditorWidget::Button::SaveChildEdits, mRelationShowSaveChildEditsCheckBox->isChecked() );
627 
628  return QVariantMap(
629  {
630  {"buttons", qgsFlagValueToKeys( buttons )},
631  {"show_first_feature", mShowFirstFeature->isChecked()}
632  } );
633 }
634 
635 void QgsRelationEditorConfigWidget::setConfig( const QVariantMap &config )
636 {
637  const QgsRelationEditorWidget::Buttons buttons = qgsFlagKeysToValue( config.value( QStringLiteral( "buttons" ) ).toString(), QgsRelationEditorWidget::Button::AllButtons );
638 
639  mRelationShowLinkCheckBox->setChecked( buttons.testFlag( QgsRelationEditorWidget::Button::Link ) );
640  mRelationShowUnlinkCheckBox->setChecked( buttons.testFlag( QgsRelationEditorWidget::Button::Unlink ) );
641  mRelationShowAddChildCheckBox->setChecked( buttons.testFlag( QgsRelationEditorWidget::Button::AddChildFeature ) );
642  mRelationShowDuplicateChildFeatureCheckBox->setChecked( buttons.testFlag( QgsRelationEditorWidget::Button::DuplicateChildFeature ) );
643  mRelationShowZoomToFeatureCheckBox->setChecked( buttons.testFlag( QgsRelationEditorWidget::Button::ZoomToChildFeature ) );
644  mRelationDeleteChildFeatureCheckBox->setChecked( buttons.testFlag( QgsRelationEditorWidget::Button::DeleteChildFeature ) );
645  mRelationShowSaveChildEditsCheckBox->setChecked( buttons.testFlag( QgsRelationEditorWidget::Button::SaveChildEdits ) );
646  mShowFirstFeature->setChecked( config.value( QStringLiteral( "show_first_feature" ), true ).toBool() );
647 }
648 
649 
651 
652 
653 #ifndef SIP_RUN
655 {
656 
657 }
658 
660 {
661  return QStringLiteral( "relation_editor" );
662 }
663 
665 {
666  return QObject::tr( "Relation Editor" );
667 }
668 
669 QgsAbstractRelationEditorWidget *QgsRelationEditorWidgetFactory::create( const QVariantMap &config, QWidget *parent ) const
670 {
671  return new QgsRelationEditorWidget( config, parent );
672 }
673 
675 {
676  return static_cast<QgsAbstractRelationEditorConfigWidget *>( new QgsRelationEditorConfigWidget( relation, parent ) );
677 }
678 #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.
void deleteFeatures(const QgsFeatureIds &fids)
Deletes the features with fids.
void linkFeature()
Links a new feature to the relation.
void addFeature(const QgsGeometry &geometry=QgsGeometry())
Adds a new feature with given geometry.
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.
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
bool isValid() const
Returns the validity of this feature.
Definition: qgsfeature.cpp:209
Is an interface class to abstract feature selection handling.
void selectionChanged(const QgsFeatureIds &selected, const QgsFeatureIds &deselected, bool clearAndSelect)
Emitted when selection was changed.
Map canvas is a class for displaying all GIS data types on a canvas.
Definition: qgsmapcanvas.h:89
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
T qgsFlagKeysToValue(const QString &keys, const T &defaultValue)
Returns the value corresponding to the given keys of a flag.
Definition: qgis.h:1460
QString qgsFlagValueToKeys(const T &value)
Returns the value for the given keys of a flag.
Definition: qgis.h:1448
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