QGIS API Documentation 3.99.0-Master (2fe06baccd8)
Loading...
Searching...
No Matches
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 "qgsactionmenu.h"
19#include "qgsapplication.h"
20#include "qgsexpression.h"
23#include "qgsfeature.h"
24#include "qgsfeatureiterator.h"
25#include "qgsiconutils.h"
26#include "qgslogger.h"
27#include "qgsmapcanvas.h"
29#include "qgsmessagebar.h"
30#include "qgsmessagebaritem.h"
31#include "qgsrelation.h"
33#include "qgsvectorlayerutils.h"
34
35#include <QHBoxLayout>
36#include <QLabel>
37#include <QMessageBox>
38#include <QPushButton>
39#include <QTreeWidget>
40
41#include "moc_qgsrelationeditorwidget.cpp"
42
45QgsFilteredSelectionManager::QgsFilteredSelectionManager( QgsVectorLayer *layer, const QgsFeatureRequest &request, QObject *parent )
46 : QgsVectorLayerSelectionManager( layer, parent )
47 , mRequest( request )
48{
49 if ( !layer )
50 return;
51
52 for ( const auto fid : layer->selectedFeatureIds() )
53 if ( mRequest.acceptFeature( layer->getFeature( fid ) ) )
54 mSelectedFeatureIds << fid;
55
56 connect( layer, &QgsVectorLayer::selectionChanged, this, &QgsFilteredSelectionManager::onSelectionChanged );
57}
58
59const QgsFeatureIds &QgsFilteredSelectionManager::selectedFeatureIds() const
60
61
62{
63 return mSelectedFeatureIds;
64}
65
66int QgsFilteredSelectionManager::selectedFeatureCount()
67{
68 return mSelectedFeatureIds.count();
69}
70
71void QgsFilteredSelectionManager::onSelectionChanged( const QgsFeatureIds &selected, const QgsFeatureIds &deselected, bool clearAndSelect )
72{
73 QgsFeatureIds lselected = selected;
74 if ( clearAndSelect )
75 {
76 mSelectedFeatureIds.clear();
77 }
78 else
79 {
80 for ( const auto fid : deselected )
81 mSelectedFeatureIds.remove( fid );
82 }
83
84 for ( const auto fid : selected )
85 if ( mRequest.acceptFeature( layer()->getFeature( fid ) ) )
86 mSelectedFeatureIds << fid;
87 else
88 lselected.remove( fid );
89
90 emit selectionChanged( lselected, deselected, clearAndSelect );
91}
92
94
95QgsRelationEditorWidget::QgsRelationEditorWidget( const QVariantMap &config, QWidget *parent )
97 , mButtonsVisibility( qgsFlagKeysToValue( config.value( QStringLiteral( "buttons" ) ).toString(), QgsRelationEditorWidget::Button::AllButtons ) )
98 , mShowFirstFeature( config.value( QStringLiteral( "show_first_feature" ), true ).toBool() )
99 , mAllowAddChildFeatureWithNoGeometry( config.value( QStringLiteral( "allow_add_child_feature_with_no_geometry" ), false ).toBool() )
100 , mFilterExpression( config.value( QStringLiteral( "filter_expression" ) ).toString() )
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(s)" ) );
139 mDuplicateFeatureButton->setToolTip( tr( "Duplicate selected child feature(s)" ) );
140 mDuplicateFeatureButton->setObjectName( QStringLiteral( "mDuplicateFeatureButton" ) );
141 buttonLayout->addWidget( mDuplicateFeatureButton );
142 // delete feature
143 mDeleteFeatureButton = new QToolButton( this );
144 mDeleteFeatureButton->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mActionDeleteSelectedFeatures.svg" ) ) );
145 mDeleteFeatureButton->setText( tr( "Delete Child Feature(s)" ) );
146 mDeleteFeatureButton->setToolTip( tr( "Delete selected child feature(s)" ) );
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 Feature(s)" ) );
153 mLinkFeatureButton->setToolTip( tr( "Link existing child feature(s)" ) );
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(s)" ) );
160 mUnlinkFeatureButton->setToolTip( tr( "Unlink selected child feature(s)" ) );
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(s)" ) );
167 mZoomToFeatureButton->setToolTip( tr( "Zoom to selected child feature(s)" ) );
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 // multiedit info label
193 mMultiEditInfoLabel = new QLabel( this );
194 buttonLayout->addWidget( mMultiEditInfoLabel );
195
196 // add buttons layout
197 rootLayout->addLayout( buttonLayout );
198
199 // add stacked widget
200 mStackedWidget = new QStackedWidget( this );
201
202 // add dual view (single feature content)
203 mDualView = new QgsDualView( this );
204 mDualView->setView( mViewMode );
205 connect( mDualView, &QgsDualView::showContextMenuExternally, this, &QgsRelationEditorWidget::showContextMenu );
206
207 // add multi feature editing page
208 mMultiEditStackedWidgetPage = new QWidget( this );
209 {
210 QVBoxLayout *vBoxLayout = new QVBoxLayout();
211 vBoxLayout->setContentsMargins( 0, 0, 0, 0 );
212
213 mMultiEditTreeWidget = new QTreeWidget( this );
214 mMultiEditTreeWidget->setHeaderHidden( true );
215 mMultiEditTreeWidget->setSelectionMode( QTreeWidget::ExtendedSelection );
216 vBoxLayout->addWidget( mMultiEditTreeWidget );
217
218 mMultiEditStackedWidgetPage->setLayout( vBoxLayout );
219 }
220 mStackedWidget->addWidget( mMultiEditStackedWidgetPage );
221
222 mStackedWidget->addWidget( mDualView );
223
224 rootLayout->addWidget( mStackedWidget );
225
226 connect( mViewModeButtonGroup, &QButtonGroup::idClicked, this, static_cast<void ( QgsRelationEditorWidget::* )( int )>( &QgsRelationEditorWidget::setViewMode ) );
227 connect( mToggleEditingButton, &QAbstractButton::clicked, this, &QgsRelationEditorWidget::toggleEditing );
228 connect( mSaveEditsButton, &QAbstractButton::clicked, this, &QgsRelationEditorWidget::saveEdits );
229 connect( mAddFeatureButton, &QAbstractButton::clicked, this, &QgsRelationEditorWidget::addFeature );
230 connect( mAddFeatureGeometryButton, &QAbstractButton::clicked, this, &QgsRelationEditorWidget::addFeatureGeometry );
231 connect( mDuplicateFeatureButton, &QAbstractButton::clicked, this, &QgsRelationEditorWidget::duplicateSelectedFeatures );
232 connect( mDeleteFeatureButton, &QAbstractButton::clicked, this, &QgsRelationEditorWidget::deleteSelectedFeatures );
233 connect( mLinkFeatureButton, &QAbstractButton::clicked, this, &QgsRelationEditorWidget::linkFeature );
234 connect( mUnlinkFeatureButton, &QAbstractButton::clicked, this, &QgsRelationEditorWidget::unlinkSelectedFeatures );
235 connect( mZoomToFeatureButton, &QAbstractButton::clicked, this, &QgsRelationEditorWidget::zoomToSelectedFeatures );
236 connect( mMultiEditTreeWidget, &QTreeWidget::itemSelectionChanged, this, &QgsRelationEditorWidget::multiEditItemSelectionChanged );
237
238 // Set initial state for add/remove etc. buttons
239 updateButtons();
240
241 setLayout( rootLayout );
242}
243
244void QgsRelationEditorWidget::initDualView( QgsVectorLayer *layer, const QgsFeatureRequest &request )
245{
246 if ( multiEditModeActive() )
247 {
248 QgsLogger::warning( tr( "Dual view should not be used in multiple edit mode" ) );
249 return;
250 }
251
252 QgsAttributeEditorContext ctx { mEditorContext };
253 ctx.setParentFormFeature( mFeatureList.first() );
254 mDualView->init( layer, mEditorContext.mapCanvas(), request, ctx, true, mShowFirstFeature );
255 mFeatureSelectionMgr = new QgsFilteredSelectionManager( layer, request, mDualView );
256 mDualView->setFeatureSelectionManager( mFeatureSelectionMgr );
257
258 connect( mFeatureSelectionMgr, &QgsIFeatureSelectionManager::selectionChanged, this, &QgsRelationEditorWidget::updateButtons );
259
260 QIcon icon;
261 QString text;
262 if ( layer->geometryType() == Qgis::GeometryType::Point )
263 {
264 icon = QgsApplication::getThemeIcon( QStringLiteral( "/mActionCapturePoint.svg" ) );
265 text = tr( "Add Point Child Feature" );
266 }
267 else if ( layer->geometryType() == Qgis::GeometryType::Line )
268 {
269 icon = QgsApplication::getThemeIcon( QStringLiteral( "/mActionCaptureLine.svg" ) );
270 text = tr( "Add Line Child Feature" );
271 }
272 else if ( layer->geometryType() == Qgis::GeometryType::Polygon )
273 {
274 icon = QgsApplication::getThemeIcon( QStringLiteral( "/mActionCapturePolygon.svg" ) );
275 text = tr( "Add Polygon Child Feature" );
276 }
277
278 mAddFeatureGeometryButton->setIcon( icon );
279 mAddFeatureGeometryButton->setText( text );
280 mAddFeatureGeometryButton->setToolTip( text );
281
282 updateButtons();
283}
284
286{
287 mEditorContext = context;
288
289 if ( context.mapCanvas() && context.cadDockWidget() )
290 {
291 mMapToolDigitize.reset( new QgsMapToolDigitizeFeature( context.mapCanvas(), context.cadDockWidget() ) );
292 mMapToolDigitize->setButton( mAddFeatureGeometryButton );
293 }
294
295 updateButtons();
296}
297
299{
300 mDualView->setView( mode );
301 mViewMode = mode;
302}
303
304void QgsRelationEditorWidget::updateButtons()
305{
306 bool toggleEditingButtonEnabled = false;
307 bool canAdd = false;
308 bool canAddGeometry = false;
309 bool canRemove = false;
310 bool canEdit = false;
311 bool canLink = false;
312 bool canUnlink = false;
313 bool spatial = false;
314
315 if ( mRelation.isValid() )
316 {
317 toggleEditingButtonEnabled = mRelation.referencingLayer()->supportsEditing();
319 canAddGeometry = mRelation.referencingLayer()->isEditable();
320 canRemove = mRelation.referencingLayer()->isEditable();
323 canUnlink = mRelation.referencingLayer()->isEditable();
324 spatial = mRelation.referencingLayer()->isSpatial();
325 }
326
327 if ( mNmRelation.isValid() )
328 {
329 toggleEditingButtonEnabled |= mNmRelation.referencedLayer()->supportsEditing();
330 canAdd = mNmRelation.referencedLayer()->isEditable();
331 canAddGeometry = mNmRelation.referencedLayer()->isEditable();
332 canRemove = mNmRelation.referencedLayer()->isEditable();
333 canEdit = mNmRelation.referencedLayer()->isEditable();
334 spatial = mNmRelation.referencedLayer()->isSpatial();
335 }
336
337 const bool selectionNotEmpty = mFeatureSelectionMgr ? mFeatureSelectionMgr->selectedFeatureCount() : false;
338 if ( multiEditModeActive() )
339 {
340 const bool multieditLinkedChildSelected = !selectedChildFeatureIds().isEmpty();
341
342 canAddGeometry = false;
343
344 canRemove = canRemove && multieditLinkedChildSelected;
345
346 // In 1:n relations an element can be linked only to 1 feature
347 canLink = canLink && mNmRelation.isValid();
348 canUnlink = canUnlink && multieditLinkedChildSelected;
349 }
350 else
351 {
352 canRemove = canRemove && selectionNotEmpty;
353 canUnlink = canUnlink && selectionNotEmpty;
354 }
355
356 mToggleEditingButton->setEnabled( toggleEditingButtonEnabled );
357 mAddFeatureButton->setEnabled( canAdd );
358 mAddFeatureGeometryButton->setEnabled( canAddGeometry );
359 mDuplicateFeatureButton->setEnabled( canEdit && selectionNotEmpty );
360 mLinkFeatureButton->setEnabled( canLink );
361 mDeleteFeatureButton->setEnabled( canRemove );
362 mUnlinkFeatureButton->setEnabled( canUnlink );
363 mZoomToFeatureButton->setEnabled( selectionNotEmpty );
364 mToggleEditingButton->setChecked( canEdit );
365 mSaveEditsButton->setEnabled( canEdit || canLink || canUnlink );
366
367 mToggleEditingButton->setVisible( !mLayerInSameTransactionGroup );
368
369 mLinkFeatureButton->setVisible( mButtonsVisibility.testFlag( QgsRelationEditorWidget::Button::Link ) );
370 mUnlinkFeatureButton->setVisible( mButtonsVisibility.testFlag( QgsRelationEditorWidget::Button::Unlink ) );
371 mSaveEditsButton->setVisible( mButtonsVisibility.testFlag( QgsRelationEditorWidget::Button::SaveChildEdits ) && !mLayerInSameTransactionGroup );
372 mAddFeatureButton->setVisible( mButtonsVisibility.testFlag( QgsRelationEditorWidget::Button::AddChildFeature ) && !( spatial && !mAllowAddChildFeatureWithNoGeometry ) );
373 mAddFeatureGeometryButton->setVisible( mButtonsVisibility.testFlag( QgsRelationEditorWidget::Button::AddChildFeature ) && mEditorContext.mapCanvas() && mEditorContext.cadDockWidget() && spatial );
374 mDuplicateFeatureButton->setVisible( mButtonsVisibility.testFlag( QgsRelationEditorWidget::Button::DuplicateChildFeature ) );
375 mDeleteFeatureButton->setVisible( mButtonsVisibility.testFlag( QgsRelationEditorWidget::Button::DeleteChildFeature ) );
376 mZoomToFeatureButton->setVisible( mButtonsVisibility.testFlag( QgsRelationEditorWidget::Button::ZoomToChildFeature ) && mEditorContext.mapCanvas() && spatial );
377}
378
379void QgsRelationEditorWidget::addFeature()
380{
382
383 if ( !multiEditModeActive() )
384 return;
385
386 mMultiEditTreeWidget->blockSignals( true );
387 mMultiEdit1NJustAddedIds = addedFeatures;
388 QTreeWidgetItemIterator treeWidgetItemIterator( mMultiEditTreeWidget );
389 while ( *treeWidgetItemIterator )
390 {
391 if ( ( *treeWidgetItemIterator )->data( 0, static_cast<int>( MultiEditTreeWidgetRole::FeatureType ) ).toInt() != static_cast<int>( MultiEditFeatureType::Child ) )
392 {
393 ++treeWidgetItemIterator;
394 continue;
395 }
396
397 if ( addedFeatures.contains( ( *treeWidgetItemIterator )->data( 0, static_cast<int>( MultiEditTreeWidgetRole::FeatureId ) ).toInt() ) )
398 ( *treeWidgetItemIterator )->setSelected( true );
399
400 ++treeWidgetItemIterator;
401 }
402 mMultiEditTreeWidget->blockSignals( false );
403
404 updateUi();
405 updateButtons();
406}
407
408void QgsRelationEditorWidget::addFeatureGeometry()
409{
410 if ( multiEditModeActive() )
411 {
412 QgsLogger::warning( tr( "Adding a geometry feature is not supported in multiple edit mode" ) );
413 return;
414 }
415
416 QgsVectorLayer *layer = nullptr;
417 if ( mNmRelation.isValid() )
418 layer = mNmRelation.referencedLayer();
419 else
420 layer = mRelation.referencingLayer();
421
422 mMapToolDigitize->setLayer( layer );
423
424 // window is always on top, so we hide it to digitize without seeing it
425 if ( window()->objectName() != QLatin1String( "QgisApp" ) )
426 {
427 window()->setVisible( false );
428 }
429 setMapTool( mMapToolDigitize );
430
431 connect( mMapToolDigitize, &QgsMapToolDigitizeFeature::digitizingCompleted, this, &QgsRelationEditorWidget::onDigitizingCompleted );
432
433 connect( mMapToolDigitize, &QgsMapToolDigitizeFeature::digitizingCanceled, this, &QgsRelationEditorWidget::onDigitizingCanceled );
434
435 if ( auto *lMainMessageBar = mEditorContext.mainMessageBar() )
436 {
437 const QString displayString = QgsVectorLayerUtils::getFeatureDisplayString( layer, mFeatureList.first() );
438
439 const QString title = tr( "Create child feature for parent %1 \"%2\"" ).arg( mRelation.referencedLayer()->name(), displayString );
440 const QString msg = tr( "Digitize the geometry for the new feature on layer %1. Press &lt;ESC&gt; to cancel." )
441 .arg( layer->name() );
442 mMessageBarItem = QgsMessageBar::createMessage( title, msg, this );
443 lMainMessageBar->pushItem( mMessageBarItem );
444 }
445}
446
447void QgsRelationEditorWidget::onDigitizingCompleted( const QgsFeature &feature )
448{
450 digitizingFinished();
451}
452
453void QgsRelationEditorWidget::multiEditItemSelectionChanged()
454{
455 const QList<QTreeWidgetItem *> selectedItems = mMultiEditTreeWidget->selectedItems();
456
457 // Select all items pointing to the same feature
458 // but only if we are not deselecting.
459 if ( selectedItems.size() == 1
460 && mMultiEditPreviousSelectedItems.size() <= 1 )
461 {
462 if ( selectedItems.first()->data( 0, static_cast<int>( MultiEditTreeWidgetRole::FeatureType ) ).toInt() == static_cast<int>( MultiEditFeatureType::Child ) )
463 {
464 mMultiEditTreeWidget->blockSignals( true );
465
466 const QgsFeatureId featureIdSelectedItem = selectedItems.first()->data( 0, static_cast<int>( MultiEditTreeWidgetRole::FeatureId ) ).toInt();
467
468 QTreeWidgetItemIterator treeWidgetItemIterator( mMultiEditTreeWidget );
469 while ( *treeWidgetItemIterator )
470 {
471 if ( ( *treeWidgetItemIterator )->data( 0, static_cast<int>( MultiEditTreeWidgetRole::FeatureType ) ).toInt() != static_cast<int>( MultiEditFeatureType::Child ) )
472 {
473 ++treeWidgetItemIterator;
474 continue;
475 }
476
477 const QgsFeatureId featureIdCurrentItem = ( *treeWidgetItemIterator )->data( 0, static_cast<int>( MultiEditTreeWidgetRole::FeatureId ) ).toInt();
478 if ( mNmRelation.isValid() )
479 {
480 if ( featureIdSelectedItem == featureIdCurrentItem )
481 ( *treeWidgetItemIterator )->setSelected( true );
482 }
483 else
484 {
485 if ( !mMultiEdit1NJustAddedIds.contains( featureIdSelectedItem ) )
486 break;
487
488 if ( mMultiEdit1NJustAddedIds.contains( featureIdCurrentItem ) )
489 ( *treeWidgetItemIterator )->setSelected( true );
490 }
491
492 ++treeWidgetItemIterator;
493 }
494 mMultiEditTreeWidget->blockSignals( false );
495 }
496 }
497 mMultiEditPreviousSelectedItems = selectedItems;
498 updateButtons();
499}
500
501void QgsRelationEditorWidget::linkFeature()
502{
504}
505
506void QgsRelationEditorWidget::toggleEditing( bool state ) // cppcheck-suppress duplInheritedMember
507{
509
510 updateButtons();
511}
512
514{
515 if ( !mRelation.isValid() || mFeatureList.isEmpty() || !mFeatureList.first().isValid() )
516 return;
517
518 if ( !isVisible() )
519 return;
520
521 if ( multiEditModeActive() )
522 updateUiMultiEdit();
523 else
524 updateUiSingleEdit();
525}
526
528{
529 mButtonsVisibility = buttons;
530 updateButtons();
531}
532
534{
535 Buttons buttons;
536 if ( mLinkFeatureButton->isVisible() )
537 buttons |= Button::Link;
538 if ( mUnlinkFeatureButton->isVisible() )
539 buttons |= Button::Unlink;
540 if ( mSaveEditsButton->isVisible() )
541 buttons |= Button::SaveChildEdits;
542 if ( mAddFeatureButton->isVisible() )
543 buttons |= Button::AddChildFeature;
544 if ( mDuplicateFeatureButton->isVisible() )
546 if ( mDeleteFeatureButton->isVisible() )
548 if ( mZoomToFeatureButton->isVisible() )
550 return buttons;
551}
552
553void QgsRelationEditorWidget::parentFormValueChanged( const QString &attribute, const QVariant &newValue )
554{
555 mDualView->parentFormValueChanged( attribute, newValue );
556}
557
558void QgsRelationEditorWidget::showContextMenu( QgsActionMenu *menu, const QgsFeatureId fid )
559{
561 {
562 QAction *qAction = nullptr;
563
564 if ( mButtonsVisibility.testFlag( QgsRelationEditorWidget::Button::DeleteChildFeature ) )
565 {
566 qAction = menu->addAction( QgsApplication::getThemeIcon( QStringLiteral( "/mActionDeleteSelected.svg" ) ), tr( "Delete Feature" ) );
567 connect( qAction, &QAction::triggered, this, [this, fid]() { deleteFeature( fid ); } );
568 }
569
570 if ( mButtonsVisibility.testFlag( QgsRelationEditorWidget::Button::Unlink ) )
571 {
572 qAction = menu->addAction( QgsApplication::getThemeIcon( QStringLiteral( "/mActionUnlink.svg" ) ), tr( "Unlink Feature" ) );
573 connect( qAction, &QAction::triggered, this, [this, fid]() { unlinkFeature( fid ); } );
574 }
575 }
576}
577
578void QgsRelationEditorWidget::setMapTool( QgsMapTool *mapTool )
579{
580 QgsMapCanvas *mapCanvas = mEditorContext.mapCanvas();
581
582 mapCanvas->setMapTool( mapTool );
583 mapCanvas->window()->raise();
584 mapCanvas->activateWindow();
585 mapCanvas->setFocus();
586 connect( mapTool, &QgsMapTool::deactivated, this, &QgsRelationEditorWidget::mapToolDeactivated );
587}
588
589void QgsRelationEditorWidget::unsetMapTool()
590{
591 QgsMapCanvas *mapCanvas = mEditorContext.mapCanvas();
592
593 // this will call mapToolDeactivated
594 mapCanvas->unsetMapTool( mMapToolDigitize );
595
596 disconnect( mMapToolDigitize, &QgsMapToolDigitizeFeature::digitizingCompleted, this, &QgsRelationEditorWidget::onDigitizingCompleted );
597}
598
599QgsFeatureIds QgsRelationEditorWidget::selectedChildFeatureIds() const
600{
601 if ( multiEditModeActive() )
602 {
603 QgsFeatureIds featureIds;
604 for ( QTreeWidgetItem *treeWidgetItem : mMultiEditTreeWidget->selectedItems() )
605 {
606 if ( static_cast<MultiEditFeatureType>( treeWidgetItem->data( 0, static_cast<int>( MultiEditTreeWidgetRole::FeatureType ) ).toInt() ) != MultiEditFeatureType::Child )
607 continue;
608
609 featureIds.insert( treeWidgetItem->data( 0, static_cast<int>( MultiEditTreeWidgetRole::FeatureId ) ).toLongLong() );
610 }
611 return featureIds;
612 }
613 else if ( mFeatureSelectionMgr )
614 {
615 return mFeatureSelectionMgr->selectedFeatureIds();
616 }
617 else
618 {
619 return {};
620 }
621}
622
623void QgsRelationEditorWidget::updateUiSingleEdit()
624{
625 mFormViewButton->setVisible( true );
626 mTableViewButton->setVisible( true );
627 mMultiEditInfoLabel->setVisible( false );
628
629 mStackedWidget->setCurrentWidget( mDualView );
630
631 QgsFeatureRequest request = mRelation.getRelatedFeaturesRequest( mFeatureList.first() );
632 QgsVectorLayer *layer = nullptr;
633 if ( mNmRelation.isValid() )
634 {
635 QgsFeature fet;
636 QgsFeatureRequest nmRequest;
637
638 QgsFeatureIterator it = mRelation.referencingLayer()->getFeatures( request );
639 QStringList filters;
640
641 while ( it.nextFeature( fet ) )
642 {
643 QString filter = mNmRelation.getReferencedFeatureRequest( fet ).filterExpression()->expression();
644 filters << filter.prepend( '(' ).append( ')' );
645 }
646
647 QString reducedExpression;
648 if ( QgsExpression::attemptReduceToInClause( filters, reducedExpression ) )
649 {
650 nmRequest.setFilterExpression( reducedExpression );
651 }
652 else
653 {
654 nmRequest.setFilterExpression( filters.join( QLatin1String( " OR " ) ) );
655 }
656
657 request = std::move( nmRequest );
658 layer = mNmRelation.referencedLayer();
659 }
660 else if ( mRelation.referencingLayer() )
661 {
662 layer = mRelation.referencingLayer();
663 }
664
665 if ( !layer )
666 return;
667
668 // don't recreate all the widget from scratch if only the request has changed
669 if ( !mDualView->masterModel() || layer != mDualView->masterModel()->layer() )
670 {
671 initDualView( layer, request );
672 }
673 else
674 {
675 mFeatureSelectionMgr = new QgsFilteredSelectionManager( layer, request, mDualView );
676 mDualView->setFeatureSelectionManager( mFeatureSelectionMgr );
677 connect( mFeatureSelectionMgr, &QgsIFeatureSelectionManager::selectionChanged, this, &QgsRelationEditorWidget::updateButtons );
678
679 mDualView->setRequest( request );
680 mDualView->masterModel()->loadLayer();
681
682 updateButtons();
683 }
684}
685
686void QgsRelationEditorWidget::updateUiMultiEdit()
687{
688 mFormViewButton->setVisible( false );
689 mTableViewButton->setVisible( false );
690 mMultiEditInfoLabel->setVisible( true );
691
692 mStackedWidget->setCurrentWidget( mMultiEditStackedWidgetPage );
693
694 QList<QTreeWidgetItem *> parentTreeWidgetItems;
695
696 QgsFeatureIds featureIdsMixedValues;
697 QMultiMap<QTreeWidgetItem *, QgsFeatureId> multimapChildFeatures;
698
699 mMultiEditTreeWidget->clear();
700 for ( const QgsFeature &featureParent : std::as_const( mFeatureList ) )
701 {
702 QTreeWidgetItem *treeWidgetItem = createMultiEditTreeWidgetItem( featureParent, mRelation.referencedLayer(), MultiEditFeatureType::Parent );
703
704 // Parent feature items are not selectable
705 treeWidgetItem->setFlags( Qt::ItemIsEnabled );
706
707 parentTreeWidgetItems.append( treeWidgetItem );
708
709 // Get child features
710 const QgsFeatureRequest request = relation().getRelatedFeaturesRequest( featureParent );
711 QgsFeatureIterator featureIterator = mRelation.referencingLayer()->getFeatures( request );
712 QgsFeature featureChild;
713 while ( featureIterator.nextFeature( featureChild ) )
714 {
715 if ( mNmRelation.isValid() )
716 {
717 const QgsFeatureRequest requestFinalChild = mNmRelation.getReferencedFeatureRequest( featureChild );
718 QgsFeatureIterator featureIteratorFinalChild = mNmRelation.referencedLayer()->getFeatures( requestFinalChild );
719 QgsFeature featureChildChild;
720 while ( featureIteratorFinalChild.nextFeature( featureChildChild ) )
721 {
722 QTreeWidgetItem *treeWidgetItemChild = createMultiEditTreeWidgetItem( featureChildChild, mNmRelation.referencedLayer(), MultiEditFeatureType::Child );
723
724 treeWidgetItem->addChild( treeWidgetItemChild );
725
726 featureIdsMixedValues.insert( featureChildChild.id() );
727 multimapChildFeatures.insert( treeWidgetItem, featureChildChild.id() );
728 }
729 }
730 else
731 {
732 QTreeWidgetItem *treeWidgetItemChild = createMultiEditTreeWidgetItem( featureChild, mRelation.referencingLayer(), MultiEditFeatureType::Child );
733 treeWidgetItem->addChild( treeWidgetItemChild );
734
735 featureIdsMixedValues.insert( featureChild.id() );
736 }
737 }
738
739 mMultiEditTreeWidget->addTopLevelItem( treeWidgetItem );
740 treeWidgetItem->setExpanded( true );
741 }
742
743 // Set mixed values indicator (Green or Orange)
744 //
745 // Green:
746 // n:m and 1:n: 0 child features available
747 // n:m with no mixed values
748 // Orange:
749 // n:m with mixed values
750 // 1:n always, including when we pseudo know that feature are related (just added feature)
751 //
752 // See https://github.com/qgis/QGIS/pull/45703
753 //
754 if ( mNmRelation.isValid() )
755 {
756 QgsFeatureIds::iterator iterator = featureIdsMixedValues.begin();
757 while ( iterator != featureIdsMixedValues.end() )
758 {
759 bool mixedValues = false;
760 for ( QTreeWidgetItem *parentTreeWidgetItem : parentTreeWidgetItems )
761 {
762 if ( !multimapChildFeatures.values( parentTreeWidgetItem ).contains( *iterator ) )
763 {
764 mixedValues = true;
765 break;
766 }
767 }
768
769 if ( !mixedValues )
770 {
771 iterator = featureIdsMixedValues.erase( iterator );
772 continue;
773 }
774
775 ++iterator;
776 }
777 }
778
779 // Set multiedit info label
780 if ( featureIdsMixedValues.isEmpty() )
781 {
782 QIcon icon = QgsApplication::getThemeIcon( QStringLiteral( "/multieditSameValues.svg" ) );
783 mMultiEditInfoLabel->setPixmap( icon.pixmap( mMultiEditInfoLabel->height(), mMultiEditInfoLabel->height() ) );
784 mMultiEditInfoLabel->setToolTip( tr( "All features in selection have equal relations" ) );
785 }
786 else
787 {
788 QIcon icon = QgsApplication::getThemeIcon( QStringLiteral( "/multieditMixedValues.svg" ) );
789 mMultiEditInfoLabel->setPixmap( icon.pixmap( mMultiEditInfoLabel->height(), mMultiEditInfoLabel->height() ) );
790 mMultiEditInfoLabel->setToolTip( tr( "Some features in selection have different relations" ) );
791
792 // Set italic font for mixed values
793 QFont fontItalic = mMultiEditTreeWidget->font();
794 fontItalic.setItalic( true );
795 for ( QTreeWidgetItem *parentTreeWidgetItem : parentTreeWidgetItems )
796 {
797 for ( int childIndex = 0; childIndex < parentTreeWidgetItem->childCount(); ++childIndex )
798 {
799 QTreeWidgetItem *childItem = parentTreeWidgetItem->child( childIndex );
800 const QgsFeatureId featureIdCurrentItem = childItem->data( 0, static_cast<int>( MultiEditTreeWidgetRole::FeatureId ) ).toInt();
801 if ( featureIdsMixedValues.contains( featureIdCurrentItem ) )
802 childItem->setFont( 0, fontItalic );
803 }
804 }
805 }
806}
807
808QTreeWidgetItem *QgsRelationEditorWidget::createMultiEditTreeWidgetItem( const QgsFeature &feature, QgsVectorLayer *layer, MultiEditFeatureType type )
809{
810 QTreeWidgetItem *treeWidgetItem = new QTreeWidgetItem();
811 treeWidgetItem->setText( 0, QgsVectorLayerUtils::getFeatureDisplayString( layer, feature ) );
812 treeWidgetItem->setIcon( 0, QgsIconUtils::iconForLayer( layer ) );
813 treeWidgetItem->setData( 0, static_cast<int>( MultiEditTreeWidgetRole::FeatureType ), static_cast<int>( type ) );
814 treeWidgetItem->setData( 0, static_cast<int>( MultiEditTreeWidgetRole::FeatureId ), feature.id() );
815 return treeWidgetItem;
816}
817
818void QgsRelationEditorWidget::onDigitizingCanceled()
819{
820 digitizingFinished();
821}
822
823void QgsRelationEditorWidget::digitizingFinished()
824{
825 window()->setVisible( true );
826 window()->raise();
827 window()->activateWindow();
828 unsetMapTool();
829}
830
831void QgsRelationEditorWidget::mapToolDeactivated()
832{
833 if ( mEditorContext.mainMessageBar() && mMessageBarItem )
834 {
835 mEditorContext.mainMessageBar()->popWidget( mMessageBarItem );
836 }
837 mMessageBarItem = nullptr;
838}
839
841{
842 return QVariantMap( { { "buttons", qgsFlagValueToKeys( visibleButtons() ) }, { "show_first_feature", mShowFirstFeature }, { "allow_add_child_feature_with_no_geometry", mAllowAddChildFeatureWithNoGeometry }, { "filter_expression", mFilterExpression } } );
843}
844
846{
847 mButtonsVisibility = qgsFlagKeysToValue( config.value( QStringLiteral( "buttons" ) ).toString(), QgsRelationEditorWidget::Button::AllButtons );
848 mShowFirstFeature = config.value( QStringLiteral( "show_first_feature" ), true ).toBool();
849 mAllowAddChildFeatureWithNoGeometry = config.value( QStringLiteral( "allow_add_child_feature_with_no_geometry" ), false ).toBool();
850 mFilterExpression = config.value( QStringLiteral( "filter_expression" ) ).toString();
851 updateButtons();
852}
853
855{
856 Q_UNUSED( newRelation );
857 Q_UNUSED( newFeature );
858
859 if ( !mRelation.isValid() )
860 return;
861
862 disconnect( mRelation.referencingLayer(), &QgsVectorLayer::editingStarted, this, &QgsRelationEditorWidget::updateButtons );
863 disconnect( mRelation.referencingLayer(), &QgsVectorLayer::editingStopped, this, &QgsRelationEditorWidget::updateButtons );
864}
865
867{
868 if ( !mRelation.isValid()
869 || mFeatureList.isEmpty() )
870 {
871 updateButtons();
872 return;
873 }
874
875 connect( mRelation.referencingLayer(), &QgsVectorLayer::editingStarted, this, &QgsRelationEditorWidget::updateButtons );
876 connect( mRelation.referencingLayer(), &QgsVectorLayer::editingStopped, this, &QgsRelationEditorWidget::updateButtons );
877
878 updateButtons();
879
880 const QgsFeatureRequest myRequest = mRelation.getRelatedFeaturesRequest( mFeatureList.first() );
881 initDualView( mRelation.referencingLayer(), myRequest );
882}
883
884void QgsRelationEditorWidget::beforeSetRelations( const QgsRelation &newRelation, const QgsRelation &newNmRelation )
885{
886 Q_UNUSED( newRelation );
887 Q_UNUSED( newNmRelation );
888
889 if ( mRelation.isValid() )
890 {
891 disconnect( mRelation.referencingLayer(), &QgsVectorLayer::editingStarted, this, &QgsRelationEditorWidget::updateButtons );
892 disconnect( mRelation.referencingLayer(), &QgsVectorLayer::editingStopped, this, &QgsRelationEditorWidget::updateButtons );
893 }
894
895 if ( mNmRelation.isValid() )
896 {
897 disconnect( mNmRelation.referencedLayer(), &QgsVectorLayer::editingStarted, this, &QgsRelationEditorWidget::updateButtons );
898 disconnect( mNmRelation.referencedLayer(), &QgsVectorLayer::editingStopped, this, &QgsRelationEditorWidget::updateButtons );
899 }
900}
901
903{
904 if ( !mRelation.isValid() )
905 return;
906
907 connect( mRelation.referencingLayer(), &QgsVectorLayer::editingStarted, this, &QgsRelationEditorWidget::updateButtons );
908 connect( mRelation.referencingLayer(), &QgsVectorLayer::editingStopped, this, &QgsRelationEditorWidget::updateButtons );
909
910 if ( mNmRelation.isValid() )
911 {
912 connect( mNmRelation.referencedLayer(), &QgsVectorLayer::editingStarted, this, &QgsRelationEditorWidget::updateButtons );
913 connect( mNmRelation.referencedLayer(), &QgsVectorLayer::editingStopped, this, &QgsRelationEditorWidget::updateButtons );
914 }
915
916 updateButtons();
917}
918
923
925{
926 const QgsFeatureIds selectedFids = selectedChildFeatureIds();
927 unlinkFeatures( selectedFids );
928}
929
931{
932 duplicateFeatures( mFeatureSelectionMgr->selectedFeatureIds() );
933}
934
936{
937 duplicateFeatures( mFeatureSelectionMgr->selectedFeatureIds() );
938}
939
941{
942 const QgsFeatureIds selectedFids = selectedChildFeatureIds();
943 deleteFeatures( selectedFids );
944}
945
947{
948 QgsMapCanvas *c = mEditorContext.mapCanvas();
949 if ( !c )
950 return;
951
952 c->zoomToFeatureIds(
953 mNmRelation.isValid() ? mNmRelation.referencedLayer() : mRelation.referencingLayer(),
954 mFeatureSelectionMgr->selectedFeatureIds()
955 );
956}
957
959
960
963{
964 setupUi( this );
965 connect( mEditExpression, &QAbstractButton::clicked, this, &QgsRelationEditorConfigWidget::mEditExpression_clicked );
966
967 // Make filter depending on link button
968 filterExpressionLabel->setEnabled( mRelationShowLinkCheckBox->isChecked() );
969 mEditExpression->setEnabled( mRelationShowLinkCheckBox->isChecked() );
970 mFilterExpression->setEnabled( mRelationShowLinkCheckBox->isChecked() );
971 connect( mRelationShowLinkCheckBox, &QCheckBox::toggled, filterExpressionLabel, &QLabel::setEnabled );
972 connect( mRelationShowLinkCheckBox, &QCheckBox::toggled, mEditExpression, &QToolButton::setEnabled );
973 connect( mRelationShowLinkCheckBox, &QCheckBox::toggled, mFilterExpression, &QTextEdit::setEnabled );
974
975 // Make add feature with no geometry depending on add button
976 mAllowAddChildFeatureWithNoGeometry->setEnabled( mRelationShowAddChildCheckBox->isChecked() );
977 connect( mRelationShowAddChildCheckBox, &QCheckBox::toggled, mAllowAddChildFeatureWithNoGeometry, &QCheckBox::setEnabled );
978}
979
981{
982 QgsVectorLayer *vl = nullptr;
983
984 if ( nmRelation().isValid() )
985 {
987 }
988 else
989 {
990 vl = relation().referencingLayer();
991 }
992
993 // Show expression builder
995 QgsExpressionBuilderDialog dlg( vl, mFilterExpression->toPlainText(), this, QStringLiteral( "generic" ), context );
996 dlg.setWindowTitle( tr( "Edit Filter Expression of Target Layer" ) );
997
998 if ( dlg.exec() == QDialog::Accepted )
999 {
1000 mFilterExpression->setPlainText( dlg.expressionBuilder()->expressionText() );
1001 }
1002}
1003
1005{
1007 buttons.setFlag( QgsRelationEditorWidget::Button::Link, mRelationShowLinkCheckBox->isChecked() );
1008 buttons.setFlag( QgsRelationEditorWidget::Button::Unlink, mRelationShowUnlinkCheckBox->isChecked() );
1009 buttons.setFlag( QgsRelationEditorWidget::Button::AddChildFeature, mRelationShowAddChildCheckBox->isChecked() );
1010 buttons.setFlag( QgsRelationEditorWidget::Button::DuplicateChildFeature, mRelationShowDuplicateChildFeatureCheckBox->isChecked() );
1011 buttons.setFlag( QgsRelationEditorWidget::Button::ZoomToChildFeature, mRelationShowZoomToFeatureCheckBox->isChecked() );
1012 buttons.setFlag( QgsRelationEditorWidget::Button::DeleteChildFeature, mRelationDeleteChildFeatureCheckBox->isChecked() );
1013 buttons.setFlag( QgsRelationEditorWidget::Button::SaveChildEdits, mRelationShowSaveChildEditsCheckBox->isChecked() );
1014
1015 return QVariantMap(
1016 { { "buttons", qgsFlagValueToKeys( buttons ) },
1017 { "show_first_feature", mShowFirstFeature->isChecked() },
1018 { "allow_add_child_feature_with_no_geometry", mAllowAddChildFeatureWithNoGeometry->isChecked() },
1019 { "filter_expression", mFilterExpression->toPlainText() }
1020 }
1021 );
1022}
1023
1025{
1026 const QgsRelationEditorWidget::Buttons buttons = qgsFlagKeysToValue( config.value( QStringLiteral( "buttons" ) ).toString(), QgsRelationEditorWidget::Button::AllButtons );
1027
1028 mRelationShowLinkCheckBox->setChecked( buttons.testFlag( QgsRelationEditorWidget::Button::Link ) );
1029 mRelationShowUnlinkCheckBox->setChecked( buttons.testFlag( QgsRelationEditorWidget::Button::Unlink ) );
1030 mRelationShowAddChildCheckBox->setChecked( buttons.testFlag( QgsRelationEditorWidget::Button::AddChildFeature ) );
1031 mRelationShowDuplicateChildFeatureCheckBox->setChecked( buttons.testFlag( QgsRelationEditorWidget::Button::DuplicateChildFeature ) );
1032 mRelationShowZoomToFeatureCheckBox->setChecked( buttons.testFlag( QgsRelationEditorWidget::Button::ZoomToChildFeature ) );
1033 mRelationDeleteChildFeatureCheckBox->setChecked( buttons.testFlag( QgsRelationEditorWidget::Button::DeleteChildFeature ) );
1034 mRelationShowSaveChildEditsCheckBox->setChecked( buttons.testFlag( QgsRelationEditorWidget::Button::SaveChildEdits ) );
1035 mShowFirstFeature->setChecked( config.value( QStringLiteral( "show_first_feature" ), true ).toBool() );
1036 mAllowAddChildFeatureWithNoGeometry->setChecked( config.value( QStringLiteral( "allow_add_child_feature_with_no_geometry" ), false ).toBool() );
1037 mFilterExpression->setPlainText( config.value( QStringLiteral( "filter_expression" ) ).toString() );
1038}
1039
1040
1042
1043
1044#ifndef SIP_RUN
1048
1050{
1051 return QStringLiteral( "relation_editor" );
1052}
1053
1055{
1056 return QObject::tr( "Relation Editor" );
1057}
1058
1059QgsAbstractRelationEditorWidget *QgsRelationEditorWidgetFactory::create( const QVariantMap &config, QWidget *parent ) const
1060{
1061 return new QgsRelationEditorWidget( config, parent );
1062}
1063
1065{
1066 return static_cast<QgsAbstractRelationEditorConfigWidget *>( new QgsRelationEditorConfigWidget( relation, parent ) );
1067}
1068#endif
@ Point
Points.
Definition qgis.h:359
@ Line
Lines.
Definition qgis.h:360
@ Polygon
Polygons.
Definition qgis.h:361
Abstract base class for configurable relation widget types.
QgsRelation relation() const
Returns the relation for which this configuration widget applies.
virtual QgsRelation nmRelation() const
Returns the nm relation for which this configuration widget applies.
QgsAbstractRelationEditorConfigWidget(const QgsRelation &relation, QWidget *parent)
Create a new configuration widget.
Base class to build new relation widgets.
void toggleEditing(bool state)
Toggles editing state of the widget.
QgsFeatureIds addFeature(const QgsGeometry &geometry=QgsGeometry())
Adds new features with given geometry Returns the Id of added features.
void deleteFeatures(const QgsFeatureIds &fids)
Deletes the features with fids.
void linkFeature(const QString &filterExpression=QString())
Links a new feature to the relation.
QgsAbstractRelationEditorWidget(const QVariantMap &config, QWidget *parent=nullptr)
Constructor.
QgsRelation relation() const
Returns the relation.
void unlinkFeatures(const QgsFeatureIds &fids)
Unlinks the features with fids.
void deleteFeature(QgsFeatureId fid=QgsFeatureId())
Delete a feature with given fid.
QgsFeature feature() const
Returns the widget's current feature If the widget is in multiedit mode only the first is returned.
bool multiEditModeActive() const
Returns true if editing multiple features at a time.
void saveEdits()
Saves the current modifications in the relation.
void unlinkFeature(QgsFeatureId fid=QgsFeatureId())
Unlinks a feature with given fid.
void duplicateFeatures(const QgsFeatureIds &fids)
Duplicates features.
A menu that is populated automatically with the actions defined for a given layer.
static QIcon getThemeIcon(const QString &name, const QColor &fillColor=QColor(), const QColor &strokeColor=QColor())
Helper to get a theme icon.
Contains context information for attribute editor widgets.
QgsMapCanvas * mapCanvas() const
Returns the associated map canvas (e.g.
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:47
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:57
@ AttributeTable
Shows the features and attributes in a table layout.
Definition qgsdualview.h:62
@ AttributeEditor
Show a list of the features, where one can be chosen and the according attribute dialog will be prese...
Definition qgsdualview.h:69
A generic dialog for building expression strings.
QgsExpressionBuilderWidget * expressionBuilder()
The builder widget that is used by the dialog.
QString expressionText()
Gets the expression string that has been set in the expression area.
static QList< QgsExpressionContextScope * > globalProjectLayerScopes(const QgsMapLayer *layer)
Creates a list of three scopes: global, layer's project and layer.
Expression contexts are used to encapsulate the parameters around which a QgsExpression should be eva...
static bool attemptReduceToInClause(const QStringList &expressions, QString &result)
Attempts to reduce a list of expressions to a single "field IN (val1, val2, ... )" type expression.
bool nextFeature(QgsFeature &f)
Fetch next feature and stores in f, returns true on success.
Wraps a request for features to a vector layer (or directly its vector data provider).
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:58
QgsFeatureId id
Definition qgsfeature.h:66
Is an interface class to abstract feature selection handling.
void selectionChanged(const QgsFeatureIds &selected, const QgsFeatureIds &deselected, bool clearAndSelect)
Emitted when selection was changed.
static QIcon iconForLayer(const QgsMapLayer *layer)
Returns the icon corresponding to a specified map layer.
static void warning(const QString &msg)
Goes to qWarning.
Map canvas is a class for displaying all GIS data types on a canvas.
void unsetMapTool(QgsMapTool *mapTool)
Unset the current map tool or last non zoom tool.
void setMapTool(QgsMapTool *mapTool, bool clean=false)
Sets the map tool currently being used on the canvas.
QString name
Definition qgsmaplayer.h:84
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.
void digitizingCanceled()
Emitted when the digitizing process was interrupted by the user.
void digitizingCompleted(const QgsFeature &feature)
Emitted whenever the digitizing has been successfully completed.
Abstract base class for all map tools.
Definition qgsmaptool.h:72
void deactivated()
Emitted when the map tool is deactivated.
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.
QVariantMap config() override
Create a configuration from the current GUI state.
void setConfig(const QVariantMap &config) override
Update the configuration widget to represent the given configuration.
QgsRelationEditorConfigWidget(const QgsRelation &relation, QWidget *parent)
Create a new configuration widget.
void mEditExpression_clicked()
Opens an expression dialog and sets its value as filter expression for the linking dialog.
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.
@ ZoomToChildFeature
Zoom to child feature.
@ DeleteChildFeature
Delete child feature button.
@ DuplicateChildFeature
Duplicate child feature.
@ SaveChildEdits
Save child edits button.
@ AddChildFeature
Add child feature (as in some projects we only want to allow linking/unlinking existing features).
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...
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.
Represents a relationship between two vector layers.
Definition qgsrelation.h:42
QgsVectorLayer * referencedLayer
Definition qgsrelation.h:50
QgsVectorLayer * referencingLayer
Definition qgsrelation.h:47
QgsFeatureRequest getRelatedFeaturesRequest(const QgsFeature &feature) const
Creates a request to return all the features on the referencing (child) layer which have a foreign ke...
static QString getFeatureDisplayString(const QgsVectorLayer *layer, const QgsFeature &feature)
Returns a descriptive string for a feature, suitable for displaying to the user.
Represents a vector layer which manages a vector based dataset.
bool isEditable() const final
Returns true if the provider is in editing mode.
bool isSpatial() const final
Returns true if this is a geometry layer and false in case of NoGeometry (table only) or UnknownGeome...
Q_INVOKABLE const QgsFeatureIds & selectedFeatureIds() const
Returns a list of the selected features IDs in this layer.
Q_INVOKABLE Qgis::GeometryType geometryType() const
Returns point, line or polygon.
void selectionChanged(const QgsFeatureIds &selected, const QgsFeatureIds &deselected, bool clearAndSelect)
Emitted when selection was changed.
Q_INVOKABLE QgsFeature getFeature(QgsFeatureId fid) const
Queries the layer for the feature with the given id.
As part of the API refactoring and improvements which landed in the Processing API was substantially reworked from the x version This was done in order to allow much of the underlying Processing framework to be ported into c
QString qgsFlagValueToKeys(const T &value, bool *returnOk=nullptr)
Returns the value for the given keys of a flag.
Definition qgis.h:6856
T qgsFlagKeysToValue(const QString &keys, const T &defaultValue, bool tryValueAsKey=true, bool *returnOk=nullptr)
Returns the value corresponding to the given keys of a flag.
Definition qgis.h:6878
QSet< QgsFeatureId > QgsFeatureIds
qint64 QgsFeatureId
64 bit feature ids negative numbers are used for uncommitted/newly added features