QGIS API Documentation  3.18.1-Zürich (202f1bf7e5)
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Modules Pages
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 
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 
63 {
64  return mSelectedFeatureIds;
65 }
66 
67 int QgsFilteredSelectionManager::selectedFeatureCount()
68 {
69  return mSelectedFeatureIds.count();
70 }
71 
72 void QgsFilteredSelectionManager::onSelectionChanged( const QgsFeatureIds &selected, const QgsFeatureIds &deselected, bool clearAndSelect )
73 {
74  QgsFeatureIds lselected = selected;
75  if ( clearAndSelect )
76  {
77  mSelectedFeatureIds.clear();
78  }
79  else
80  {
81  for ( auto fid : deselected )
82  mSelectedFeatureIds.remove( fid );
83  }
84 
85  for ( auto fid : selected )
86  if ( mRequest.acceptFeature( layer()->getFeature( fid ) ) )
87  mSelectedFeatureIds << fid;
88  else
89  lselected.remove( fid );
90 
91  emit selectionChanged( lselected, deselected, clearAndSelect );
92 }
93 
95 
96 QgsRelationEditorWidget::QgsRelationEditorWidget( const QVariantMap &config, QWidget *parent )
97  : QgsAbstractRelationEditorWidget( config, parent )
98  , mButtonsVisibility( qgsFlagKeysToValue( config.value( QStringLiteral( "buttons" ) ).toString(), QgsRelationEditorWidget::Button::AllButtons ) )
99 {
100  QVBoxLayout *rootLayout = new QVBoxLayout( this );
101  rootLayout->setContentsMargins( 0, 0, 0, 0 );
102 
103  mRootCollapsibleGroupBox = new QgsCollapsibleGroupBox( QString(), this );
104  rootLayout->addWidget( mRootCollapsibleGroupBox );
105 
106  QVBoxLayout *topLayout = new QVBoxLayout( mRootCollapsibleGroupBox );
107  topLayout->setContentsMargins( 0, 9, 0, 0 );
108 
109  // buttons
110  QHBoxLayout *buttonLayout = new QHBoxLayout();
111  buttonLayout->setContentsMargins( 0, 0, 0, 0 );
112  // toggle editing
113  mToggleEditingButton = new QToolButton( this );
114  mToggleEditingButton->setObjectName( QStringLiteral( "mToggleEditingButton" ) );
115  mToggleEditingButton->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mActionToggleEditing.svg" ) ) );
116  mToggleEditingButton->setText( tr( "Toggle Editing" ) );
117  mToggleEditingButton->setEnabled( false );
118  mToggleEditingButton->setCheckable( true );
119  mToggleEditingButton->setToolTip( tr( "Toggle editing mode for child layer" ) );
120  buttonLayout->addWidget( mToggleEditingButton );
121  // save Edits
122  mSaveEditsButton = new QToolButton( this );
123  mSaveEditsButton->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mActionSaveEdits.svg" ) ) );
124  mSaveEditsButton->setText( tr( "Save Child Layer Edits" ) );
125  mSaveEditsButton->setToolTip( tr( "Save child layer edits" ) );
126  mSaveEditsButton->setEnabled( true );
127  buttonLayout->addWidget( mSaveEditsButton );
128  // add feature with geometry
129  mAddFeatureGeometryButton = new QToolButton( this );
130  mAddFeatureGeometryButton->setObjectName( QStringLiteral( "mAddFeatureGeometryButton" ) );
131  buttonLayout->addWidget( mAddFeatureGeometryButton );
132  // add feature
133  mAddFeatureButton = new QToolButton( this );
134  mAddFeatureButton->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mActionNewTableRow.svg" ) ) );
135  mAddFeatureButton->setText( tr( "Add Child Feature" ) );
136  mAddFeatureButton->setToolTip( tr( "Add child feature" ) );
137  mAddFeatureButton->setObjectName( QStringLiteral( "mAddFeatureButton" ) );
138  buttonLayout->addWidget( mAddFeatureButton );
139  // duplicate feature
140  mDuplicateFeatureButton = new QToolButton( this );
141  mDuplicateFeatureButton->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mActionDuplicateFeature.svg" ) ) );
142  mDuplicateFeatureButton->setText( tr( "Duplicate Child Feature" ) );
143  mDuplicateFeatureButton->setToolTip( tr( "Duplicate child feature" ) );
144  mDuplicateFeatureButton->setObjectName( QStringLiteral( "mDuplicateFeatureButton" ) );
145  buttonLayout->addWidget( mDuplicateFeatureButton );
146  // delete feature
147  mDeleteFeatureButton = new QToolButton( this );
148  mDeleteFeatureButton->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mActionDeleteSelected.svg" ) ) );
149  mDeleteFeatureButton->setText( tr( "Delete Child Feature" ) );
150  mDeleteFeatureButton->setToolTip( tr( "Delete child feature" ) );
151  mDeleteFeatureButton->setObjectName( QStringLiteral( "mDeleteFeatureButton" ) );
152  buttonLayout->addWidget( mDeleteFeatureButton );
153  // link feature
154  mLinkFeatureButton = new QToolButton( this );
155  mLinkFeatureButton->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mActionLink.svg" ) ) );
156  mLinkFeatureButton->setText( tr( "Link Existing Features" ) );
157  mLinkFeatureButton->setToolTip( tr( "Link existing child features" ) );
158  mLinkFeatureButton->setObjectName( QStringLiteral( "mLinkFeatureButton" ) );
159  buttonLayout->addWidget( mLinkFeatureButton );
160  // unlink feature
161  mUnlinkFeatureButton = new QToolButton( this );
162  mUnlinkFeatureButton->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mActionUnlink.svg" ) ) );
163  mUnlinkFeatureButton->setText( tr( "Unlink Feature" ) );
164  mUnlinkFeatureButton->setToolTip( tr( "Unlink child feature" ) );
165  mUnlinkFeatureButton->setObjectName( QStringLiteral( "mUnlinkFeatureButton" ) );
166  buttonLayout->addWidget( mUnlinkFeatureButton );
167  // zoom to linked feature
168  mZoomToFeatureButton = new QToolButton( this );
169  mZoomToFeatureButton->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mActionZoomToSelected.svg" ) ) );
170  mZoomToFeatureButton->setText( tr( "Zoom To Feature" ) );
171  mZoomToFeatureButton->setToolTip( tr( "Zoom to child feature" ) );
172  mZoomToFeatureButton->setObjectName( QStringLiteral( "mZoomToFeatureButton" ) );
173  buttonLayout->addWidget( mZoomToFeatureButton );
174  // spacer
175  buttonLayout->addItem( new QSpacerItem( 0, 0, QSizePolicy::Expanding ) );
176  // form view
177  mFormViewButton = new QToolButton( this );
178  mFormViewButton->setText( tr( "Form View" ) );
179  mFormViewButton->setToolTip( tr( "Switch to form view" ) );
180  mFormViewButton->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mActionPropertyItem.svg" ) ) );
181  mFormViewButton->setCheckable( true );
182  mFormViewButton->setChecked( mViewMode == QgsDualView::AttributeEditor );
183  buttonLayout->addWidget( mFormViewButton );
184  // table view
185  mTableViewButton = new QToolButton( this );
186  mTableViewButton->setText( tr( "Table View" ) );
187  mTableViewButton->setToolTip( tr( "Switch to table view" ) );
188  mTableViewButton->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mActionOpenTable.svg" ) ) );
189  mTableViewButton->setCheckable( true );
190  mTableViewButton->setChecked( mViewMode == QgsDualView::AttributeTable );
191  buttonLayout->addWidget( mTableViewButton );
192  // button group
193  mViewModeButtonGroup = new QButtonGroup( this );
194  mViewModeButtonGroup->addButton( mFormViewButton, QgsDualView::AttributeEditor );
195  mViewModeButtonGroup->addButton( mTableViewButton, QgsDualView::AttributeTable );
196 
197  // add buttons layout
198  topLayout->addLayout( buttonLayout );
199 
200  mRelationLayout = new QGridLayout();
201  mRelationLayout->setContentsMargins( 0, 0, 0, 0 );
202  topLayout->addLayout( mRelationLayout );
203 
204  mDualView = new QgsDualView( this );
205  mDualView->setView( mViewMode );
206 
207  mRelationLayout->addWidget( mDualView );
208 
209  connect( mRootCollapsibleGroupBox, &QgsCollapsibleGroupBoxBasic::collapsedStateChanged, this, &QgsRelationEditorWidget::onCollapsedStateChanged );
210  connect( mViewModeButtonGroup, static_cast<void ( QButtonGroup::* )( int )>( &QButtonGroup::buttonClicked ),
211  this, static_cast<void ( QgsRelationEditorWidget::* )( int )>( &QgsRelationEditorWidget::setViewMode ) );
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  connect( mDualView, &QgsDualView::showContextMenuExternally, this, &QgsRelationEditorWidget::showContextMenu );
223 
224  // Set initial state for add/remove etc. buttons
225  updateButtons();
226 }
227 
228 void QgsRelationEditorWidget::initDualView( QgsVectorLayer *layer, const QgsFeatureRequest &request )
229 {
232  mDualView->init( layer, mEditorContext.mapCanvas(), request, ctx );
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 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 editable = false;
285  bool linkable = false;
286  bool spatial = false;
287  bool selectionNotEmpty = mFeatureSelectionMgr ? mFeatureSelectionMgr->selectedFeatureCount() : false;
288 
289  if ( mRelation.isValid() )
290  {
291  editable = mRelation.referencingLayer()->isEditable();
292  linkable = mRelation.referencingLayer()->isEditable();
293  spatial = mRelation.referencingLayer()->isSpatial();
294  }
295 
296  if ( mNmRelation.isValid() )
297  {
298  editable = mNmRelation.referencedLayer()->isEditable();
299  spatial = mNmRelation.referencedLayer()->isSpatial();
300  }
301 
302  mAddFeatureButton->setEnabled( editable );
303  mAddFeatureGeometryButton->setEnabled( editable );
304  mDuplicateFeatureButton->setEnabled( editable && selectionNotEmpty );
305  mLinkFeatureButton->setEnabled( linkable );
306  mDeleteFeatureButton->setEnabled( editable && selectionNotEmpty );
307  mUnlinkFeatureButton->setEnabled( linkable && selectionNotEmpty );
308  mZoomToFeatureButton->setEnabled( selectionNotEmpty );
309  mToggleEditingButton->setChecked( editable );
310  mSaveEditsButton->setEnabled( editable );
311 
312  mToggleEditingButton->setVisible( !mLayerInSameTransactionGroup );
313 
314  mLinkFeatureButton->setVisible( mButtonsVisibility.testFlag( QgsRelationEditorWidget::Button::Link ) );
315  mUnlinkFeatureButton->setVisible( mButtonsVisibility.testFlag( QgsRelationEditorWidget::Button::Unlink ) );
316  mSaveEditsButton->setVisible( mButtonsVisibility.testFlag( QgsRelationEditorWidget::Button::SaveChildEdits ) && !mLayerInSameTransactionGroup );
317  mAddFeatureButton->setVisible( mButtonsVisibility.testFlag( QgsRelationEditorWidget::Button::AddChildFeature ) );
318  mAddFeatureGeometryButton->setVisible( mButtonsVisibility.testFlag( QgsRelationEditorWidget::Button::AddChildFeature ) && mEditorContext.mapCanvas() && mEditorContext.cadDockWidget() && spatial );
319  mDuplicateFeatureButton->setVisible( mButtonsVisibility.testFlag( QgsRelationEditorWidget::Button::DuplicateChildFeature ) );
320  mDeleteFeatureButton->setVisible( mButtonsVisibility.testFlag( QgsRelationEditorWidget::Button::DeleteChildFeature ) );
321  mZoomToFeatureButton->setVisible( mButtonsVisibility.testFlag( QgsRelationEditorWidget::Button::ZoomToChildFeature ) && mEditorContext.mapCanvas() && spatial );
322 }
323 
324 void QgsRelationEditorWidget::addFeatureGeometry()
325 {
326  QgsVectorLayer *layer = nullptr;
327  if ( mNmRelation.isValid() )
328  layer = mNmRelation.referencedLayer();
329  else
330  layer = mRelation.referencingLayer();
331 
332  mMapToolDigitize->setLayer( layer );
333 
334  // window is always on top, so we hide it to digitize without seeing it
335  window()->setVisible( false );
336  setMapTool( mMapToolDigitize );
337 
338  connect( mMapToolDigitize, &QgsMapToolDigitizeFeature::digitizingCompleted, this, &QgsRelationEditorWidget::onDigitizingCompleted );
339  connect( mEditorContext.mapCanvas(), &QgsMapCanvas::keyPressed, this, &QgsRelationEditorWidget::onKeyPressed );
340 
341  if ( auto *lMainMessageBar = mEditorContext.mainMessageBar() )
342  {
343  QString displayString = QgsVectorLayerUtils::getFeatureDisplayString( layer, mFeature );
344 
345  QString title = tr( "Create child feature for parent %1 \"%2\"" ).arg( mRelation.referencedLayer()->name(), displayString );
346  QString msg = tr( "Digitize the geometry for the new feature on layer %1. Press &lt;ESC&gt; to cancel." )
347  .arg( layer->name() );
348  mMessageBarItem = QgsMessageBar::createMessage( title, msg, this );
349  lMainMessageBar->pushItem( mMessageBarItem );
350  }
351 
352 }
353 
354 void QgsRelationEditorWidget::onDigitizingCompleted( const QgsFeature &feature )
355 {
357 
358  unsetMapTool();
359 }
360 
361 void QgsRelationEditorWidget::toggleEditing( bool state )
362 {
364 
365  updateButtons();
366 }
367 
368 void QgsRelationEditorWidget::onCollapsedStateChanged( bool collapsed )
369 {
370  if ( !collapsed )
371  {
372  mVisible = true;
373  updateUi();
374  }
375 }
376 
378 {
379  // If not yet initialized, it is not (yet) visible, so we don't load it to be faster (lazy loading)
380  // If it is already initialized, it has been set visible before and the currently shown feature is changing
381  // and the widget needs updating
382 
383  if ( mVisible )
384  {
386 
387  if ( mNmRelation.isValid() )
388  {
390 
391  QgsFeature fet;
392 
393  QStringList filters;
394 
395  while ( it.nextFeature( fet ) )
396  {
398  filters << filter.prepend( '(' ).append( ')' );
399  }
400 
401  QgsFeatureRequest nmRequest;
402 
403  nmRequest.setFilterExpression( filters.join( QLatin1String( " OR " ) ) );
404 
405  initDualView( mNmRelation.referencedLayer(), nmRequest );
406  }
407  else if ( mRelation.referencingLayer() )
408  {
409  initDualView( mRelation.referencingLayer(), myRequest );
410  }
411  }
412 }
413 
414 void QgsRelationEditorWidget::setVisibleButtons( const Buttons &buttons )
415 {
416  mButtonsVisibility = buttons;
417  updateButtons();
418 }
419 
420 QgsRelationEditorWidget::Buttons QgsRelationEditorWidget::visibleButtons() const
421 {
422  Buttons buttons;
423  if ( mLinkFeatureButton->isVisible() )
424  buttons |= Button::Link;
425  if ( mUnlinkFeatureButton->isVisible() )
426  buttons |= Button::Unlink;
427  if ( mSaveEditsButton->isVisible() )
428  buttons |= Button::SaveChildEdits;
429  if ( mAddFeatureButton->isVisible() )
430  buttons |= Button::AddChildFeature;
431  if ( mDuplicateFeatureButton->isVisible() )
432  buttons |= Button::DuplicateChildFeature;
433  if ( mDeleteFeatureButton->isVisible() )
434  buttons |= Button::DeleteChildFeature;
435  if ( mZoomToFeatureButton->isVisible() )
436  buttons |= Button::ZoomToChildFeature;
437  return buttons;
438 }
439 
440 void QgsRelationEditorWidget::parentFormValueChanged( const QString &attribute, const QVariant &newValue )
441 {
442  mDualView->parentFormValueChanged( attribute, newValue );
443 }
444 
445 void QgsRelationEditorWidget::showContextMenu( QgsActionMenu *menu, const QgsFeatureId fid )
446 {
448  {
449  QAction *qAction = nullptr;
450 
451  qAction = menu->addAction( QgsApplication::getThemeIcon( QStringLiteral( "/mActionDeleteSelected.svg" ) ), tr( "Delete Feature" ) );
452  connect( qAction, &QAction::triggered, this, [this, fid]() { deleteFeature( fid ); } );
453 
454  qAction = menu->addAction( QgsApplication::getThemeIcon( QStringLiteral( "/mActionUnlink.svg" ) ), tr( "Unlink Feature" ) );
455  connect( qAction, &QAction::triggered, this, [this, fid]() { unlinkFeature( fid ); } );
456  }
457 }
458 
459 void QgsRelationEditorWidget::setMapTool( QgsMapTool *mapTool )
460 {
461  QgsMapCanvas *mapCanvas = mEditorContext.mapCanvas();
462 
463  mapCanvas->setMapTool( mapTool );
464  mapCanvas->window()->raise();
465  mapCanvas->activateWindow();
466  mapCanvas->setFocus();
467  connect( mapTool, &QgsMapTool::deactivated, this, &QgsRelationEditorWidget::mapToolDeactivated );
468 }
469 
470 void QgsRelationEditorWidget::unsetMapTool()
471 {
472  QgsMapCanvas *mapCanvas = mEditorContext.mapCanvas();
473 
474  // this will call mapToolDeactivated
475  mapCanvas->unsetMapTool( mMapToolDigitize );
476 
477  disconnect( mapCanvas, &QgsMapCanvas::keyPressed, this, &QgsRelationEditorWidget::onKeyPressed );
478  disconnect( mMapToolDigitize, &QgsMapToolDigitizeFeature::digitizingCompleted, this, &QgsRelationEditorWidget::onDigitizingCompleted );
479 }
480 
481 void QgsRelationEditorWidget::onKeyPressed( QKeyEvent *e )
482 {
483  if ( e->key() == Qt::Key_Escape )
484  {
485  unsetMapTool();
486  }
487 }
488 
489 void QgsRelationEditorWidget::mapToolDeactivated()
490 {
491  window()->setVisible( true );
492  window()->raise();
493  window()->activateWindow();
494 
495  if ( mEditorContext.mainMessageBar() && mMessageBarItem )
496  {
497  mEditorContext.mainMessageBar()->popWidget( mMessageBarItem );
498  }
499  mMessageBarItem = nullptr;
500 }
501 
503 {
504  return QVariantMap( {{"buttons", qgsFlagValueToKeys( visibleButtons() )}} );
505 }
506 
507 void QgsRelationEditorWidget::setConfig( const QVariantMap &config )
508 {
509  mButtonsVisibility = qgsFlagKeysToValue( config.value( QStringLiteral( "buttons" ) ).toString(), QgsRelationEditorWidget::Button::AllButtons );
510  updateButtons();
511 }
512 
513 void QgsRelationEditorWidget::setTitle( const QString &title )
514 {
515  mRootCollapsibleGroupBox->setTitle( title );
516 }
517 
518 void QgsRelationEditorWidget::beforeSetRelationFeature( const QgsRelation &newRelation, const QgsFeature &newFeature )
519 {
520  Q_UNUSED( newRelation );
521  Q_UNUSED( newFeature );
522 
523  if ( ! mRelation.isValid() )
524  return;
525 
526  disconnect( mRelation.referencingLayer(), &QgsVectorLayer::editingStarted, this, &QgsRelationEditorWidget::updateButtons );
527  disconnect( mRelation.referencingLayer(), &QgsVectorLayer::editingStopped, this, &QgsRelationEditorWidget::updateButtons );
528 }
529 
531 {
532  mToggleEditingButton->setEnabled( false );
533 
534  if ( ! mRelation.isValid() )
535  return;
536 
537  connect( mRelation.referencingLayer(), &QgsVectorLayer::editingStarted, this, &QgsRelationEditorWidget::updateButtons );
538  connect( mRelation.referencingLayer(), &QgsVectorLayer::editingStopped, this, &QgsRelationEditorWidget::updateButtons );
539 
541  bool canChangeAttributes = vl->dataProvider()->capabilities() & QgsVectorDataProvider::ChangeAttributeValues;
542  if ( canChangeAttributes && !vl->readOnly() )
543  {
544  mToggleEditingButton->setEnabled( true );
545  updateButtons();
546  }
547  else
548  {
549  mToggleEditingButton->setEnabled( false );
550  }
551 
552  // If not yet initialized, it is not (yet) visible, so we don't load it to be faster (lazy loading)
553  // If it is already initialized, it has been set visible before and the currently shown feature is changing
554  // and the widget needs updating
555 
556  if ( mVisible )
557  {
559  initDualView( mRelation.referencingLayer(), myRequest );
560  }
561 }
562 
563 void QgsRelationEditorWidget::beforeSetRelations( const QgsRelation &newRelation, const QgsRelation &newNmRelation )
564 {
565  Q_UNUSED( newRelation );
566  Q_UNUSED( newNmRelation );
567 
568  if ( mRelation.isValid() )
569  {
570  disconnect( mRelation.referencingLayer(), &QgsVectorLayer::editingStarted, this, &QgsRelationEditorWidget::updateButtons );
571  disconnect( mRelation.referencingLayer(), &QgsVectorLayer::editingStopped, this, &QgsRelationEditorWidget::updateButtons );
572  }
573 
574  if ( mNmRelation.isValid() )
575  {
576  disconnect( mNmRelation.referencedLayer(), &QgsVectorLayer::editingStarted, this, &QgsRelationEditorWidget::updateButtons );
577  disconnect( mNmRelation.referencedLayer(), &QgsVectorLayer::editingStopped, this, &QgsRelationEditorWidget::updateButtons );
578  }
579 }
580 
582 {
583  if ( !mRelation.isValid() )
584  return;
585 
586  connect( mRelation.referencingLayer(), &QgsVectorLayer::editingStarted, this, &QgsRelationEditorWidget::updateButtons );
587  connect( mRelation.referencingLayer(), &QgsVectorLayer::editingStopped, this, &QgsRelationEditorWidget::updateButtons );
588 
589  if ( mNmRelation.isValid() )
590  {
591  connect( mNmRelation.referencedLayer(), &QgsVectorLayer::editingStarted, this, &QgsRelationEditorWidget::updateButtons );
592  connect( mNmRelation.referencedLayer(), &QgsVectorLayer::editingStopped, this, &QgsRelationEditorWidget::updateButtons );
593  }
594 
596  bool canChangeAttributes = vl->dataProvider()->capabilities() & QgsVectorDataProvider::ChangeAttributeValues;
597  if ( canChangeAttributes && !vl->readOnly() )
598  {
599  mToggleEditingButton->setEnabled( true );
600  }
601  else
602  {
603  mToggleEditingButton->setEnabled( false );
604  }
605 
606  updateButtons();
607 }
608 
610 {
611  return mFeatureSelectionMgr;
612 }
613 
615 {
616  unlinkFeatures( mFeatureSelectionMgr->selectedFeatureIds() );
617 }
618 
620 {
621  duplicateFeatures( mFeatureSelectionMgr->selectedFeatureIds() );
622 }
623 
625 {
626  duplicateFeatures( mFeatureSelectionMgr->selectedFeatureIds() );
627 }
628 
630 {
631  QgsFeatureIds selectedFids = mFeatureSelectionMgr->selectedFeatureIds();
632  deleteFeatures( selectedFids );
633 }
634 
636 {
638  if ( !c )
639  return;
640 
641  c->zoomToFeatureIds(
643  mFeatureSelectionMgr->selectedFeatureIds()
644  );
645 }
646 
648 
649 
651  : QgsAbstractRelationEditorConfigWidget( relation, parent )
652 {
653  setupUi( this );
654 }
655 
657 {
658  QgsRelationEditorWidget::Buttons buttons;
659  buttons.setFlag( QgsRelationEditorWidget::Button::Link, mRelationShowLinkCheckBox->isChecked() );
660  buttons.setFlag( QgsRelationEditorWidget::Button::Unlink, mRelationShowUnlinkCheckBox->isChecked() );
661  buttons.setFlag( QgsRelationEditorWidget::Button::AddChildFeature, mRelationShowAddChildCheckBox->isChecked() );
662  buttons.setFlag( QgsRelationEditorWidget::Button::DuplicateChildFeature, mRelationShowDuplicateChildFeatureCheckBox->isChecked() );
663  buttons.setFlag( QgsRelationEditorWidget::Button::ZoomToChildFeature, mRelationShowZoomToFeatureCheckBox->isChecked() );
664  buttons.setFlag( QgsRelationEditorWidget::Button::DeleteChildFeature, mRelationDeleteChildFeatureCheckBox->isChecked() );
665  buttons.setFlag( QgsRelationEditorWidget::Button::SaveChildEdits, mRelationShowSaveChildEditsCheckBox->isChecked() );
666 
667  return QVariantMap(
668  {
669  {"buttons", qgsFlagValueToKeys( buttons )},
670  } );
671 }
672 
673 void QgsRelationEditorConfigWidget::setConfig( const QVariantMap &config )
674 {
675  const QgsRelationEditorWidget::Buttons buttons = qgsFlagKeysToValue( config.value( QStringLiteral( "buttons" ) ).toString(), QgsRelationEditorWidget::Button::AllButtons );
676 
677  mRelationShowLinkCheckBox->setChecked( buttons.testFlag( QgsRelationEditorWidget::Button::Link ) );
678  mRelationShowUnlinkCheckBox->setChecked( buttons.testFlag( QgsRelationEditorWidget::Button::Unlink ) );
679  mRelationShowAddChildCheckBox->setChecked( buttons.testFlag( QgsRelationEditorWidget::Button::AddChildFeature ) );
680  mRelationShowDuplicateChildFeatureCheckBox->setChecked( buttons.testFlag( QgsRelationEditorWidget::Button::DuplicateChildFeature ) );
681  mRelationShowZoomToFeatureCheckBox->setChecked( buttons.testFlag( QgsRelationEditorWidget::Button::ZoomToChildFeature ) );
682  mRelationDeleteChildFeatureCheckBox->setChecked( buttons.testFlag( QgsRelationEditorWidget::Button::DeleteChildFeature ) );
683  mRelationShowSaveChildEditsCheckBox->setChecked( buttons.testFlag( QgsRelationEditorWidget::Button::SaveChildEdits ) );
684 }
685 
686 
688 
689 
690 #ifndef SIP_RUN
692 {
693 
694 }
695 
697 {
698  return QStringLiteral( "relation_editor" );
699 }
700 
702 {
703  return QStringLiteral( "Relation Editor" );
704 }
705 
706 QgsAbstractRelationEditorWidget *QgsRelationEditorWidgetFactory::create( const QVariantMap &config, QWidget *parent ) const
707 {
708  return new QgsRelationEditorWidget( config, parent );
709 }
710 
712 {
713  return static_cast<QgsAbstractRelationEditorConfigWidget *>( new QgsRelationEditorConfigWidget( relation, parent ) );
714 }
715 #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)
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.
void collapsedStateChanged(bool collapsed)
Signal emitted when groupbox collapsed/expanded state is changed, and when first shown.
A groupbox that collapses/expands when toggled and can save its collapsed and checked states.
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 parentFormValueChanged(const QString &attribute, const QVariant &value)
Called in embedded forms when an attribute value in the parent form has changed.
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.
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 id, geometry and a list of field/values...
Definition: qgsfeature.h:56
QgsGeometry geometry
Definition: qgsfeature.h:67
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:86
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:88
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:66
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:136
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 setTitle(const QString &title) override
Sets the title of the root groupbox.
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...
@ ChangeAttributeValues
Allows modification of attribute values.
virtual Q_INVOKABLE QgsVectorDataProvider::Capabilities capabilities() const
Returns flags containing the supported capabilities.
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.
void editingStopped()
Emitted when edited changes have been successfully written to the data provider.
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.
void editingStarted()
Emitted when editing on this layer has started.
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.
QgsVectorDataProvider * dataProvider() FINAL
Returns the layer's data provider, it may be nullptr.
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:562
QString qgsFlagValueToKeys(const T &value)
Returns the value for the given keys of a flag.
Definition: qgis.h:550
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