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