QGIS API Documentation  3.0.2-Girona (307d082)
qgsrelationeditorwidget.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgsrelationeditor.cpp
3  --------------------------------------
4  Date : 17.5.2013
5  Copyright : (C) 2013 Matthias Kuhn
6  Email : matthias at opengis dot ch
7  ***************************************************************************
8  * *
9  * This program is free software; you can redistribute it and/or modify *
10  * it under the terms of the GNU General Public License as published by *
11  * the Free Software Foundation; either version 2 of the License, or *
12  * (at your option) any later version. *
13  * *
14  ***************************************************************************/
15 
17 
18 #include "qgsapplication.h"
19 #include "qgsdistancearea.h"
20 #include "qgsfeatureiterator.h"
21 #include "qgsvectordataprovider.h"
22 #include "qgsexpression.h"
23 #include "qgsfeature.h"
24 #include "qgsfeatureselectiondlg.h"
26 #include "qgsrelation.h"
27 #include "qgsvectorlayertools.h"
28 #include "qgsproject.h"
29 #include "qgstransactiongroup.h"
30 #include "qgslogger.h"
31 #include "qgsvectorlayerutils.h"
32 
33 #include <QHBoxLayout>
34 #include <QLabel>
35 
37  : QgsCollapsibleGroupBox( parent )
38 {
39  QVBoxLayout *topLayout = new QVBoxLayout( this );
40  topLayout->setContentsMargins( 0, 9, 0, 0 );
41  setLayout( topLayout );
42 
43  // buttons
44  QHBoxLayout *buttonLayout = new QHBoxLayout();
45  buttonLayout->setContentsMargins( 0, 0, 0, 0 );
46  // toogle editing
47  mToggleEditingButton = new QToolButton( this );
48  mToggleEditingButton->setObjectName( QStringLiteral( "mToggleEditingButton" ) );
49  mToggleEditingButton->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mActionToggleEditing.svg" ) ) );
50  mToggleEditingButton->setText( tr( "Toggle editing" ) );
51  mToggleEditingButton->setEnabled( false );
52  mToggleEditingButton->setCheckable( true );
53  mToggleEditingButton->setToolTip( tr( "Toggle editing mode for child layer" ) );
54  buttonLayout->addWidget( mToggleEditingButton );
55  // save Edits
56  mSaveEditsButton = new QToolButton( this );
57  mSaveEditsButton->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mActionSaveEdits.svg" ) ) );
58  mSaveEditsButton->setText( tr( "Save child layer edits" ) );
59  mSaveEditsButton->setToolTip( tr( "Save child layer edits" ) );
60  mSaveEditsButton->setEnabled( true );
61  buttonLayout->addWidget( mSaveEditsButton );
62  // add feature
63  mAddFeatureButton = new QToolButton( this );
64  mAddFeatureButton->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mActionNewTableRow.svg" ) ) );
65  mAddFeatureButton->setText( tr( "Add child feature" ) );
66  mAddFeatureButton->setToolTip( tr( "Add child feature" ) );
67  mAddFeatureButton->setObjectName( QStringLiteral( "mAddFeatureButton" ) );
68  buttonLayout->addWidget( mAddFeatureButton );
69  // duplicate feature
70  mDuplicateFeatureButton = new QToolButton( this );
71  mDuplicateFeatureButton->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mActionDuplicateFeature.svg" ) ) );
72  mDuplicateFeatureButton->setText( tr( "Duplicate child feature" ) );
73  mDuplicateFeatureButton->setToolTip( tr( "Duplicate child feature" ) );
74  mDuplicateFeatureButton->setObjectName( QStringLiteral( "mDuplicateFeatureButton" ) );
75  buttonLayout->addWidget( mDuplicateFeatureButton );
76  // delete feature
77  mDeleteFeatureButton = new QToolButton( this );
78  mDeleteFeatureButton->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mActionDeleteSelected.svg" ) ) );
79  mDeleteFeatureButton->setText( tr( "Delete child feature" ) );
80  mDeleteFeatureButton->setToolTip( tr( "Delete child feature" ) );
81  mDeleteFeatureButton->setObjectName( QStringLiteral( "mDeleteFeatureButton" ) );
82  buttonLayout->addWidget( mDeleteFeatureButton );
83  // link feature
84  mLinkFeatureButton = new QToolButton( this );
85  mLinkFeatureButton->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mActionLink.svg" ) ) );
86  mLinkFeatureButton->setText( tr( "Link existing features" ) );
87  mLinkFeatureButton->setToolTip( tr( "Link existing child features" ) );
88  mLinkFeatureButton->setObjectName( QStringLiteral( "mLinkFeatureButton" ) );
89  buttonLayout->addWidget( mLinkFeatureButton );
90  // unlink feature
91  mUnlinkFeatureButton = new QToolButton( this );
92  mUnlinkFeatureButton->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mActionUnlink.svg" ) ) );
93  mUnlinkFeatureButton->setText( tr( "Unlink feature" ) );
94  mUnlinkFeatureButton->setToolTip( tr( "Unlink child feature" ) );
95  mUnlinkFeatureButton->setObjectName( QStringLiteral( "mUnlinkFeatureButton" ) );
96  buttonLayout->addWidget( mUnlinkFeatureButton );
97  // spacer
98  buttonLayout->addItem( new QSpacerItem( 0, 0, QSizePolicy::Expanding ) );
99  // form view
100  mFormViewButton = new QToolButton( this );
101  mFormViewButton->setText( tr( "Form view" ) );
102  mFormViewButton->setToolTip( tr( "Switch to form view" ) );
103  mFormViewButton->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mActionPropertyItem.svg" ) ) );
104  mFormViewButton->setCheckable( true );
105  mFormViewButton->setChecked( mViewMode == QgsDualView::AttributeEditor );
106  buttonLayout->addWidget( mFormViewButton );
107  // table view
108  mTableViewButton = new QToolButton( this );
109  mTableViewButton->setText( tr( "Table view" ) );
110  mTableViewButton->setToolTip( tr( "Switch to table view" ) );
111  mTableViewButton->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mActionOpenTable.svg" ) ) );
112  mTableViewButton->setCheckable( true );
113  mTableViewButton->setChecked( mViewMode == QgsDualView::AttributeTable );
114  buttonLayout->addWidget( mTableViewButton );
115  // button group
116  mViewModeButtonGroup = new QButtonGroup( this );
117  mViewModeButtonGroup->addButton( mFormViewButton, QgsDualView::AttributeEditor );
118  mViewModeButtonGroup->addButton( mTableViewButton, QgsDualView::AttributeTable );
119 
120  // add buttons layout
121  topLayout->addLayout( buttonLayout );
122 
123  mRelationLayout = new QGridLayout();
124  mRelationLayout->setContentsMargins( 0, 0, 0, 0 );
125  topLayout->addLayout( mRelationLayout );
126 
127  mDualView = new QgsDualView( this );
128  mDualView->setView( mViewMode );
129  mFeatureSelectionMgr = new QgsGenericFeatureSelectionManager( mDualView );
130  mDualView->setFeatureSelectionManager( mFeatureSelectionMgr );
131 
132  mRelationLayout->addWidget( mDualView );
133 
134  connect( this, &QgsCollapsibleGroupBoxBasic::collapsedStateChanged, this, &QgsRelationEditorWidget::onCollapsedStateChanged );
135  connect( mViewModeButtonGroup, static_cast<void ( QButtonGroup::* )( int )>( &QButtonGroup::buttonClicked ),
136  this, static_cast<void ( QgsRelationEditorWidget::* )( int )>( &QgsRelationEditorWidget::setViewMode ) );
137  connect( mToggleEditingButton, &QAbstractButton::clicked, this, &QgsRelationEditorWidget::toggleEditing );
138  connect( mSaveEditsButton, &QAbstractButton::clicked, this, &QgsRelationEditorWidget::saveEdits );
139  connect( mAddFeatureButton, &QAbstractButton::clicked, this, &QgsRelationEditorWidget::addFeature );
140  connect( mDuplicateFeatureButton, &QAbstractButton::clicked, this, &QgsRelationEditorWidget::duplicateFeature );
141  connect( mDeleteFeatureButton, &QAbstractButton::clicked, this, &QgsRelationEditorWidget::deleteSelectedFeatures );
142  connect( mLinkFeatureButton, &QAbstractButton::clicked, this, &QgsRelationEditorWidget::linkFeature );
143  connect( mUnlinkFeatureButton, &QAbstractButton::clicked, this, &QgsRelationEditorWidget::unlinkSelectedFeatures );
144  connect( mFeatureSelectionMgr, &QgsIFeatureSelectionManager::selectionChanged, this, &QgsRelationEditorWidget::updateButtons );
145 
146  connect( mDualView, &QgsDualView::showContextMenuExternally, this, &QgsRelationEditorWidget::showContextMenu );
147 
148  // Set initial state for add/remove etc. buttons
149  updateButtons();
150 }
151 
153 {
154  if ( mRelation.isValid() )
155  {
156  disconnect( mRelation.referencingLayer(), &QgsVectorLayer::editingStarted, this, &QgsRelationEditorWidget::updateButtons );
157  disconnect( mRelation.referencingLayer(), &QgsVectorLayer::editingStopped, this, &QgsRelationEditorWidget::updateButtons );
158  }
159 
160  mRelation = relation;
161  mFeature = feature;
162 
163  connect( mRelation.referencingLayer(), &QgsVectorLayer::editingStarted, this, &QgsRelationEditorWidget::updateButtons );
164  connect( mRelation.referencingLayer(), &QgsVectorLayer::editingStopped, this, &QgsRelationEditorWidget::updateButtons );
165 
166  if ( mShowLabel )
167  setTitle( relation.name() );
168 
169  QgsVectorLayer *lyr = relation.referencingLayer();
170 
171  bool canChangeAttributes = lyr->dataProvider()->capabilities() & QgsVectorDataProvider::ChangeAttributeValues;
172  if ( canChangeAttributes && !lyr->readOnly() )
173  {
174  mToggleEditingButton->setEnabled( true );
175  updateButtons();
176  }
177  else
178  {
179  mToggleEditingButton->setEnabled( false );
180  }
181 
182  setObjectName( QStringLiteral( "referenced/" ) + mRelation.name() );
183 
184  // If not yet initialized, it is not (yet) visible, so we don't load it to be faster (lazy loading)
185  // If it is already initialized, it has been set visible before and the currently shown feature is changing
186  // and the widget needs updating
187 
188  if ( mVisible )
189  {
190  QgsFeatureRequest myRequest = mRelation.getRelatedFeaturesRequest( mFeature );
191 
192  mDualView->init( mRelation.referencingLayer(), nullptr, myRequest, mEditorContext );
193  }
194 }
195 
196 void QgsRelationEditorWidget::setRelations( const QgsRelation &relation, const QgsRelation &nmrelation )
197 {
198  if ( mRelation.isValid() )
199  {
200  disconnect( mRelation.referencingLayer(), &QgsVectorLayer::editingStarted, this, &QgsRelationEditorWidget::updateButtons );
201  disconnect( mRelation.referencingLayer(), &QgsVectorLayer::editingStopped, this, &QgsRelationEditorWidget::updateButtons );
202  }
203 
204  if ( mNmRelation.isValid() )
205  {
206  disconnect( mNmRelation.referencedLayer(), &QgsVectorLayer::editingStarted, this, &QgsRelationEditorWidget::updateButtons );
207  disconnect( mNmRelation.referencedLayer(), &QgsVectorLayer::editingStopped, this, &QgsRelationEditorWidget::updateButtons );
208  }
209 
210  mRelation = relation;
211  mNmRelation = nmrelation;
212 
213  if ( !mRelation.isValid() )
214  return;
215 
216  mToggleEditingButton->setVisible( true );
217 
218  const auto transactionGroups = QgsProject::instance()->transactionGroups();
219  for ( auto it = transactionGroups.constBegin(); it != transactionGroups.constEnd(); ++it )
220  {
221  if ( it.value()->layers().contains( mRelation.referencingLayer() ) )
222  {
223  mToggleEditingButton->setVisible( false );
224  mSaveEditsButton->setVisible( false );
225  }
226  }
227 
228  connect( mRelation.referencingLayer(), &QgsVectorLayer::editingStarted, this, &QgsRelationEditorWidget::updateButtons );
229  connect( mRelation.referencingLayer(), &QgsVectorLayer::editingStopped, this, &QgsRelationEditorWidget::updateButtons );
230 
231  if ( mNmRelation.isValid() )
232  {
233  connect( mNmRelation.referencedLayer(), &QgsVectorLayer::editingStarted, this, &QgsRelationEditorWidget::updateButtons );
234  connect( mNmRelation.referencedLayer(), &QgsVectorLayer::editingStopped, this, &QgsRelationEditorWidget::updateButtons );
235  }
236 
237  setTitle( relation.name() );
238 
239  QgsVectorLayer *lyr = relation.referencingLayer();
240 
241  bool canChangeAttributes = lyr->dataProvider()->capabilities() & QgsVectorDataProvider::ChangeAttributeValues;
242  if ( canChangeAttributes && !lyr->readOnly() )
243  {
244  mToggleEditingButton->setEnabled( true );
245  updateButtons();
246  }
247  else
248  {
249  mToggleEditingButton->setEnabled( false );
250  }
251 
252  setObjectName( QStringLiteral( "referenced/" ) + mRelation.name() );
253 
254  updateUi();
255 }
256 
258 {
259  mEditorContext = context;
260 }
261 
263 {
264  return mFeatureSelectionMgr;
265 }
266 
268 {
269  mDualView->setView( mode );
270  mViewMode = mode;
271 }
272 
273 
275 {
276  mFeature = feature;
277 
278  updateUi();
279 }
280 
281 void QgsRelationEditorWidget::updateButtons()
282 {
283  bool editable = false;
284  bool linkable = false;
285  bool selectionNotEmpty = mFeatureSelectionMgr->selectedFeatureCount();
286 
287  if ( mRelation.isValid() )
288  {
289  editable = mRelation.referencingLayer()->isEditable();
290  linkable = mRelation.referencingLayer()->isEditable();
291  }
292 
293  if ( mNmRelation.isValid() )
294  {
295  editable = mNmRelation.referencedLayer()->isEditable();
296  }
297 
298  mAddFeatureButton->setEnabled( editable );
299  mDuplicateFeatureButton->setEnabled( editable && selectionNotEmpty );
300  mLinkFeatureButton->setEnabled( linkable );
301  mDeleteFeatureButton->setEnabled( editable && selectionNotEmpty );
302  mUnlinkFeatureButton->setEnabled( linkable && selectionNotEmpty );
303  mToggleEditingButton->setChecked( editable );
304  mSaveEditsButton->setEnabled( editable );
305 }
306 
307 void QgsRelationEditorWidget::addFeature()
308 {
309  QgsAttributeMap keyAttrs;
310 
311  const QgsVectorLayerTools *vlTools = mEditorContext.vectorLayerTools();
312 
313  if ( mNmRelation.isValid() )
314  {
315  // n:m Relation: first let the user create a new feature on the other table
316  // and autocreate a new linking feature.
317  QgsFeature f;
318  if ( vlTools->addFeature( mNmRelation.referencedLayer(), QgsAttributeMap(), QgsGeometry(), &f ) )
319  {
320  // Fields of the linking table
321  const QgsFields fields = mRelation.referencingLayer()->fields();
322 
323  // Expression context for the linking table
325 
326  QgsAttributeMap linkAttributes;
327  Q_FOREACH ( const QgsRelation::FieldPair &fieldPair, mRelation.fieldPairs() )
328  {
329  int index = fields.indexOf( fieldPair.first );
330  linkAttributes.insert( index, mFeature.attribute( fieldPair.second ) );
331  }
332 
333  Q_FOREACH ( const QgsRelation::FieldPair &fieldPair, mNmRelation.fieldPairs() )
334  {
335  int index = fields.indexOf( fieldPair.first );
336  linkAttributes.insert( index, f.attribute( fieldPair.second ) );
337  }
338  QgsFeature linkFeature = QgsVectorLayerUtils::createFeature( mRelation.referencingLayer(), QgsGeometry(), linkAttributes, &context );
339 
340  mRelation.referencingLayer()->addFeature( linkFeature );
341 
342  updateUi();
343  }
344  }
345  else
346  {
347  QgsFields fields = mRelation.referencingLayer()->fields();
348 
349  Q_FOREACH ( const QgsRelation::FieldPair &fieldPair, mRelation.fieldPairs() )
350  {
351  keyAttrs.insert( fields.indexFromName( fieldPair.referencingField() ), mFeature.attribute( fieldPair.referencedField() ) );
352  }
353 
354  vlTools->addFeature( mDualView->masterModel()->layer(), keyAttrs );
355  }
356 }
357 
358 void QgsRelationEditorWidget::linkFeature()
359 {
360  QgsVectorLayer *layer = nullptr;
361 
362  if ( mNmRelation.isValid() )
363  layer = mNmRelation.referencedLayer();
364  else
365  layer = mRelation.referencingLayer();
366 
367  QgsFeatureSelectionDlg selectionDlg( layer, mEditorContext, this );
368 
369  if ( selectionDlg.exec() )
370  {
371  if ( mNmRelation.isValid() )
372  {
373  QgsFeatureIterator it = mNmRelation.referencedLayer()->getFeatures(
375  .setFilterFids( selectionDlg.selectedFeatures() )
376  .setSubsetOfAttributes( mNmRelation.referencedFields() ) );
377 
378  QgsFeature relatedFeature;
379 
380  QgsFeatureList newFeatures;
381 
382  // Fields of the linking table
383  const QgsFields fields = mRelation.referencingLayer()->fields();
384 
385  // Expression context for the linking table
387 
388  QgsAttributeMap linkAttributes;
389  Q_FOREACH ( const QgsRelation::FieldPair &fieldPair, mRelation.fieldPairs() )
390  {
391  int index = fields.indexOf( fieldPair.first );
392  linkAttributes.insert( index, mFeature.attribute( fieldPair.second ) );
393  }
394 
395  while ( it.nextFeature( relatedFeature ) )
396  {
397  Q_FOREACH ( const QgsRelation::FieldPair &fieldPair, mNmRelation.fieldPairs() )
398  {
399  int index = fields.indexOf( fieldPair.first );
400  linkAttributes.insert( index, relatedFeature.attribute( fieldPair.second ) );
401  }
402  const QgsFeature linkFeature = QgsVectorLayerUtils::createFeature( mRelation.referencingLayer(), QgsGeometry(), linkAttributes, &context );
403 
404  newFeatures << linkFeature;
405  }
406 
407  mRelation.referencingLayer()->addFeatures( newFeatures );
408  QgsFeatureIds ids;
409  Q_FOREACH ( const QgsFeature &f, newFeatures )
410  ids << f.id();
411  mRelation.referencingLayer()->selectByIds( ids );
412 
413 
414  updateUi();
415  }
416  else
417  {
418  QMap<int, QVariant> keys;
419  Q_FOREACH ( const QgsRelation::FieldPair &fieldPair, mRelation.fieldPairs() )
420  {
421  int idx = mRelation.referencingLayer()->fields().lookupField( fieldPair.referencingField() );
422  QVariant val = mFeature.attribute( fieldPair.referencedField() );
423  keys.insert( idx, val );
424  }
425 
426  Q_FOREACH ( QgsFeatureId fid, selectionDlg.selectedFeatures() )
427  {
428  QMapIterator<int, QVariant> it( keys );
429  while ( it.hasNext() )
430  {
431  it.next();
432  mRelation.referencingLayer()->changeAttributeValue( fid, it.key(), it.value() );
433  }
434  }
435  }
436  }
437 }
438 
439 void QgsRelationEditorWidget::duplicateFeature()
440 {
441  QgsVectorLayer *layer = mRelation.referencingLayer();
442 
443  QgsFeatureIterator fit = layer->getFeatures( QgsFeatureRequest().setFilterFids( mFeatureSelectionMgr->selectedFeatureIds() ) );
444  QgsFeature f;
445  while ( fit.nextFeature( f ) )
446  {
447  QgsVectorLayerUtils::QgsDuplicateFeatureContext duplicatedFeatureContext;
448  QgsVectorLayerUtils::duplicateFeature( layer, f, QgsProject::instance(), 0, duplicatedFeatureContext );
449  }
450 }
451 
452 void QgsRelationEditorWidget::deleteFeature( const QgsFeatureId featureid )
453 {
454  QgsFeatureIds featureids;
455 
456  featureids << featureid;
457 
458  deleteFeatures( featureids );
459 
460 }
461 
462 void QgsRelationEditorWidget::deleteSelectedFeatures( )
463 {
464  deleteFeatures( mFeatureSelectionMgr->selectedFeatureIds() );
465 }
466 
467 void QgsRelationEditorWidget::deleteFeatures( const QgsFeatureIds &featureids )
468 {
469  QgsVectorLayer *layer = nullptr;
470  if ( mNmRelation.isValid() )
471  layer = mNmRelation.referencedLayer();
472  else
473  layer = mRelation.referencingLayer();
474 
475  QgsDebugMsg( QString( "Delete %1" ).arg( featureids.size() ) );
476  layer->deleteFeatures( featureids );
477 }
478 
479 
480 void QgsRelationEditorWidget::unlinkFeature( const QgsFeatureId featureid )
481 {
482  QgsFeatureIds featureids;
483 
484  featureids << featureid;
485 
486  unlinkFeatures( featureids );
487 }
488 
489 void QgsRelationEditorWidget::unlinkSelectedFeatures( )
490 {
491  unlinkFeatures( mFeatureSelectionMgr->selectedFeatureIds() );
492 }
493 
494 void QgsRelationEditorWidget::unlinkFeatures( const QgsFeatureIds &featureids )
495 {
496  if ( mNmRelation.isValid() )
497  {
498  QgsFeatureIterator selectedIterator = mNmRelation.referencedLayer()->getFeatures(
500  .setFilterFids( featureids )
501  .setSubsetOfAttributes( mNmRelation.referencedFields() ) );
502 
503  QgsFeature f;
504 
505  QStringList filters;
506 
507  while ( selectedIterator.nextFeature( f ) )
508  {
509  filters << '(' + mNmRelation.getRelatedFeaturesRequest( f ).filterExpression()->expression() + ')';
510  }
511 
512  QString filter = QStringLiteral( "(%1) AND (%2)" ).arg(
513  mRelation.getRelatedFeaturesRequest( mFeature ).filterExpression()->expression(),
514  filters.join( QStringLiteral( " OR " ) ) );
515 
516  QgsFeatureIterator linkedIterator = mRelation.referencingLayer()->getFeatures( QgsFeatureRequest()
517  .setSubsetOfAttributes( QgsAttributeList() )
518  .setFilterExpression( filter ) );
519 
520  QgsFeatureIds fids;
521 
522  while ( linkedIterator.nextFeature( f ) )
523  {
524  fids << f.id();
525  QgsDebugMsgLevel( FID_TO_STRING( f.id() ), 4 );
526  }
527 
528  mRelation.referencingLayer()->deleteFeatures( fids );
529 
530  updateUi();
531  }
532  else
533  {
534  QMap<int, QgsField> keyFields;
535  Q_FOREACH ( const QgsRelation::FieldPair &fieldPair, mRelation.fieldPairs() )
536  {
537  int idx = mRelation.referencingLayer()->fields().lookupField( fieldPair.referencingField() );
538  if ( idx < 0 )
539  {
540  QgsDebugMsg( QString( "referencing field %1 not found" ).arg( fieldPair.referencingField() ) );
541  return;
542  }
543  QgsField fld = mRelation.referencingLayer()->fields().at( idx );
544  keyFields.insert( idx, fld );
545  }
546 
547  Q_FOREACH ( QgsFeatureId fid, featureids )
548  {
549  QMapIterator<int, QgsField> it( keyFields );
550  while ( it.hasNext() )
551  {
552  it.next();
553  mRelation.referencingLayer()->changeAttributeValue( fid, it.key(), QVariant( it.value().type() ) );
554  }
555  }
556  }
557 }
558 
559 void QgsRelationEditorWidget::toggleEditing( bool state )
560 {
561  if ( state )
562  {
563  mEditorContext.vectorLayerTools()->startEditing( mRelation.referencingLayer() );
564  if ( mNmRelation.isValid() )
565  mEditorContext.vectorLayerTools()->startEditing( mNmRelation.referencedLayer() );
566  }
567  else
568  {
569  mEditorContext.vectorLayerTools()->stopEditing( mRelation.referencingLayer() );
570  if ( mNmRelation.isValid() )
571  mEditorContext.vectorLayerTools()->stopEditing( mNmRelation.referencedLayer() );
572  }
573 }
574 
575 void QgsRelationEditorWidget::saveEdits()
576 {
577  mEditorContext.vectorLayerTools()->saveEdits( mRelation.referencingLayer() );
578  if ( mNmRelation.isValid() )
579  mEditorContext.vectorLayerTools()->saveEdits( mNmRelation.referencedLayer() );
580 }
581 
582 void QgsRelationEditorWidget::onCollapsedStateChanged( bool collapsed )
583 {
584 
585  if ( !collapsed )
586  {
587  mVisible = true;
588  updateUi();
589  }
590 }
591 
592 void QgsRelationEditorWidget::updateUi()
593 {
594  // If not yet initialized, it is not (yet) visible, so we don't load it to be faster (lazy loading)
595  // If it is already initialized, it has been set visible before and the currently shown feature is changing
596  // and the widget needs updating
597 
598  if ( mVisible )
599  {
600  QgsFeatureRequest myRequest = mRelation.getRelatedFeaturesRequest( mFeature );
601 
602  if ( mNmRelation.isValid() )
603  {
604  QgsFeatureIterator it = mRelation.referencingLayer()->getFeatures( myRequest );
605 
606  QgsFeature fet;
607 
608  QStringList filters;
609 
610  while ( it.nextFeature( fet ) )
611  {
612  QString filter = mNmRelation.getReferencedFeatureRequest( fet ).filterExpression()->expression();
613  filters << filter.prepend( '(' ).append( ')' );
614  }
615 
616  QgsFeatureRequest nmRequest;
617 
618  nmRequest.setFilterExpression( filters.join( QStringLiteral( " OR " ) ) );
619 
620  mDualView->init( mNmRelation.referencedLayer(), nullptr, nmRequest, mEditorContext );
621  }
622  else
623  {
624  mDualView->init( mRelation.referencingLayer(), nullptr, myRequest, mEditorContext );
625  }
626  }
627 }
628 
630 {
631  return mLinkFeatureButton->isVisible();
632 }
633 
635 {
636  mLinkFeatureButton->setVisible( showLinkButton );
637 }
638 
640 {
641  return mUnlinkFeatureButton->isVisible();
642 }
643 
645 {
646  mUnlinkFeatureButton->setVisible( showUnlinkButton );
647 }
648 
650 {
651  return mShowLabel;
652 }
653 
655 {
656  mShowLabel = showLabel;
657 
658  if ( mShowLabel && mRelation.isValid() )
659  setTitle( mRelation.name() );
660  else
661  setTitle( QString() );
662 }
663 
664 void QgsRelationEditorWidget::showContextMenu( QgsActionMenu *menu, const QgsFeatureId fid )
665 {
666  if ( mRelation.referencingLayer()->isEditable() )
667  {
668  QAction *qAction = nullptr;
669 
670  qAction = menu->addAction( QgsApplication::getThemeIcon( QStringLiteral( "/mActionDeleteSelected.svg" ) ), tr( "Delete feature" ) );
671  connect( qAction, &QAction::triggered, this, [this, fid]() { deleteFeature( fid ); } );
672 
673  qAction = menu->addAction( QgsApplication::getThemeIcon( QStringLiteral( "/mActionUnlink.svg" ) ), tr( "Unlink feature" ) );
674  connect( qAction, &QAction::triggered, this, [this, fid]() { unlinkFeature( fid ); } );
675  }
676 }
int lookupField(const QString &fieldName) const
Look up field&#39;s index from the field name.
Definition: qgsfields.cpp:299
Methods in this class are used to handle basic operations on vector layers.
const QgsVectorLayerTools * vectorLayerTools() const
QString name
Definition: qgsrelation.h:45
QgsFeatureId id
Definition: qgsfeature.h:71
bool showUnlinkButton() const
Determines if the "unlink feature" button should be shown.
Wrapper for iterator of features from vector data provider or vector layer.
QgsVectorLayer * layer() const
Returns the layer this model uses as backend.
virtual bool saveEdits(QgsVectorLayer *layer) const =0
Should be called, when the features should be committed but the editing session is not ended...
static QgsFeature createFeature(QgsVectorLayer *layer, const QgsGeometry &geometry=QgsGeometry(), const QgsAttributeMap &attributes=QgsAttributeMap(), QgsExpressionContext *context=nullptr)
Creates a new feature ready for insertion into a layer.
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.
Definition: qgsdualview.cpp:62
bool collapsed
The collapsed state of this group box.
virtual QgsVectorDataProvider::Capabilities capabilities() const
Returns flags containing the supported capabilities.
virtual bool startEditing(QgsVectorLayer *layer) const =0
This will be called, whenever a vector layer should be switched to edit mode.
QgsAttributeTableModel * masterModel() const
Returns the model which has the information about all features (not only filtered) ...
Definition: qgsdualview.h:162
void setFeatureSelectionManager(QgsIFeatureSelectionManager *featureSelectionManager)
Set the feature selection model.
A groupbox that collapses/expands when toggled and can save its collapsed and checked states...
int selectedFeatureCount() override
The number of features that are selected in this layer.
Contains mainly the QMap with QgsVectorLayer and QgsFeatureIds do list all the duplicated features...
#define QgsDebugMsg(str)
Definition: qgslogger.h:38
QSet< QgsFeatureId > QgsFeatureIds
Definition: qgsfeature.h:544
This class contains context information for attribute editor widgets.
QList< QgsFeature > QgsFeatureList
Definition: qgsfeature.h:549
QgsVectorLayer referencingLayer
Definition: qgsrelation.h:43
ViewMode
The view modes, in which this widget can present information.
Definition: qgsdualview.h:54
void collapsedStateChanged(bool collapsed)
Signal emitted when groupbox collapsed/expanded state is changed, and when first shown.
#define FID_TO_STRING(fid)
Definition: qgsfeature.h:52
bool deleteFeatures(const QgsFeatureIds &fids)
Deletes a set of features from the layer (but does not commit it)
Container of fields for a vector layer.
Definition: qgsfields.h:42
A geometry is the spatial representation of a feature.
Definition: qgsgeometry.h:111
static QIcon getThemeIcon(const QString &name)
Helper to get a theme icon.
void selectByIds(const QgsFeatureIds &ids, SelectBehavior behavior=SetSelection)
Select matching features using a list of feature IDs.
bool showLinkButton() const
Determines if the "link feature" button should be shown.
virtual bool stopEditing(QgsVectorLayer *layer, bool allowCancel=true) const =0
Will be called, when an editing session is ended and the features should be committed.
The feature class encapsulates a single feature including its id, geometry and a list of field/values...
Definition: qgsfeature.h:62
void setViewMode(QgsDualView::ViewMode mode)
Define the view mode for the dual view.
bool isEditable() const override
Returns true if the provider is in editing mode.
void setView(ViewMode view)
Change the current view mode.
const QgsFeatureIds & selectedFeatures()
Get the selected features.
QgsField at(int i) const
Get field at particular index (must be in range 0..N-1)
Definition: qgsfields.cpp:145
QgsExpression * filterExpression() const
Returns the filter expression if set.
Show a list of the features, where one can be chosen and the according attribute dialog will be prese...
Definition: qgsdualview.h:67
void setShowLinkButton(bool showLinkButton)
Determines if the "link feature" button should be shown.
QgsAttributeList referencedFields() const
Returns a list of attributes used to form the referenced fields (most likely primary key) on the refe...
QgsFeatureRequest & setFilterExpression(const QString &expression)
Set the filter expression.
int indexFromName(const QString &fieldName) const
Get the field index from the field name.
Definition: qgsfields.cpp:184
QgsFeatureRequest getReferencedFeatureRequest(const QgsAttributes &attributes) const
Creates a request to return the feature on the referenced (parent) layer which is referenced by the p...
Defines a relation between matching fields of the two involved tables of a relation.
Definition: qgsrelation.h:72
QgsFields fields() const override
Returns the list of fields of this layer.
Shows the features and attributes in a table layout.
Definition: qgsdualview.h:60
#define QgsDebugMsgLevel(str, level)
Definition: qgslogger.h:39
void setShowUnlinkButton(bool showUnlinkButton)
Determines if the "unlink feature" button should be shown.
This class is a menu that is populated automatically with the actions defined for a given layer...
Definition: qgsactionmenu.h:38
Expression contexts are used to encapsulate the parameters around which a QgsExpression should be eva...
QMap< int, QVariant > QgsAttributeMap
Definition: qgsattributes.h:39
void editingStopped()
Is emitted, when edited changes successfully have been written to the data provider.
This class wraps a request for features to a vector layer (or directly its vector data provider)...
QgsRelationEditorWidget(QWidget *parent=nullptr)
QgsVectorLayer referencedLayer
Definition: qgsrelation.h:44
Encapsulate a field in an attribute table or data source.
Definition: qgsfield.h:48
QgsFeatureRequest getRelatedFeaturesRequest(const QgsFeature &feature) const
Creates a request to return all the features on the referencing (child) layer which have a foreign ke...
void editingStarted()
Is emitted, when editing on this layer has started.
QList< QgsRelation::FieldPair > fieldPairs() const
Returns the field pairs which form this relation The first element of each pair are the field names o...
QgsFeatureIterator getFeatures(const QgsFeatureRequest &request=QgsFeatureRequest()) const override
Query the layer for features specified in request.
This selection manager synchronizes a local set of selected features with an attribute table...
QgsExpressionContext createExpressionContext() const override
This method needs to be reimplemented in all classes which implement this interface and return an exp...
void setRelations(const QgsRelation &relation, const QgsRelation &nmrelation)
Set the relation(s) for this widget If only one relation is set, it will act as a simple 1:N relation...
void setRelationFeature(const QgsRelation &relation, const QgsFeature &feature)
bool isValid
Definition: qgsrelation.h:46
int indexOf(const QString &fieldName) const
Get the field index from the field name.
Definition: qgsfields.cpp:189
QMap< QPair< QString, QString >, QgsTransactionGroup * > transactionGroups()
Map of transaction groups.
QgsIFeatureSelectionManager * featureSelectionManager()
The feature selection manager is responsible for the selected features which are currently being edit...
static QgsFeature duplicateFeature(QgsVectorLayer *layer, const QgsFeature &feature, QgsProject *project, int depth, QgsDuplicateFeatureContext &duplicateFeatureContext)
Duplicates a feature and it&#39;s children (one level deep).
void setShowLabel(bool showLabel)
Defines if a title label should be shown for this widget.
static QgsProject * instance()
Returns the QgsProject singleton instance.
Definition: qgsproject.cpp:383
void setEditorContext(const QgsAttributeEditorContext &context)
QString referencedField() const
Get the name of the referenced (parent) field.
Definition: qgsrelation.h:85
QgsVectorDataProvider * dataProvider() override
Returns the layer&#39;s data provider.
bool addFeatures(QgsFeatureList &features, QgsFeatureSink::Flags flags=nullptr) override
Adds a list of features to the sink.
virtual bool addFeature(QgsVectorLayer *layer, const QgsAttributeMap &defaultValues=QgsAttributeMap(), const QgsGeometry &defaultGeometry=QgsGeometry(), QgsFeature *feature=nullptr) const =0
This method should/will be called, whenever a new feature will be added to the layer.
qint64 QgsFeatureId
Definition: qgsfeature.h:37
bool changeAttributeValue(QgsFeatureId fid, int field, const QVariant &newValue, const QVariant &oldValue=QVariant(), bool skipDefaultValues=false)
Changes an attribute value for a feature (but does not immediately commit the changes).
QList< int > QgsAttributeList
Definition: qgsfield.h:27
bool nextFeature(QgsFeature &f)
Is an interface class to abstract feature selection handling.
Represents a vector layer which manages a vector based data sets.
QVariant attribute(const QString &name) const
Lookup attribute value from attribute name.
Definition: qgsfeature.cpp:255
QString referencingField() const
Get the name of the referencing (child) field.
Definition: qgsrelation.h:83
Allows modification of attribute values.
const QgsFeatureIds & selectedFeatureIds() const override
Return reference to identifiers of selected features.
void setFeature(const QgsFeature &feature)
bool addFeature(QgsFeature &feature, QgsFeatureSink::Flags flags=nullptr) override
Adds a single feature to the sink.
void selectionChanged(const QgsFeatureIds &selected, const QgsFeatureIds &deselected, const bool clearAndSelect)
This signal is emitted when selection was changed.
This widget is used to show the attributes of a set of features of a QgsVectorLayer.
Definition: qgsdualview.h:43
bool showLabel() const
Defines if a title label should be shown for this widget.
void showContextMenuExternally(QgsActionMenu *menu, const QgsFeatureId fid)
Emitted when selecting context menu on the feature list to create the context menu individually...