QGIS API Documentation 3.99.0-Master (d270888f95f)
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 <QString>
40#include <QTreeWidget>
41
42#include "moc_qgsrelationeditorwidget.cpp"
43
44using namespace Qt::StringLiterals;
45
48QgsFilteredSelectionManager::QgsFilteredSelectionManager( QgsVectorLayer *layer, const QgsFeatureRequest &request, QObject *parent )
49 : QgsVectorLayerSelectionManager( layer, parent )
50 , mRequest( request )
51{
52 if ( !layer )
53 return;
54
55 for ( const auto fid : layer->selectedFeatureIds() )
56 if ( mRequest.acceptFeature( layer->getFeature( fid ) ) )
57 mSelectedFeatureIds << fid;
58
59 connect( layer, &QgsVectorLayer::selectionChanged, this, &QgsFilteredSelectionManager::onSelectionChanged );
60}
61
62const QgsFeatureIds &QgsFilteredSelectionManager::selectedFeatureIds() const
63
64
65{
66 return mSelectedFeatureIds;
67}
68
69int QgsFilteredSelectionManager::selectedFeatureCount()
70{
71 return mSelectedFeatureIds.count();
72}
73
74void QgsFilteredSelectionManager::onSelectionChanged( const QgsFeatureIds &selected, const QgsFeatureIds &deselected, bool clearAndSelect )
75{
76 QgsFeatureIds lselected = selected;
77 if ( clearAndSelect )
78 {
79 mSelectedFeatureIds.clear();
80 }
81 else
82 {
83 for ( const auto fid : deselected )
84 mSelectedFeatureIds.remove( fid );
85 }
86
87 for ( const auto fid : selected )
88 if ( mRequest.acceptFeature( layer()->getFeature( fid ) ) )
89 mSelectedFeatureIds << fid;
90 else
91 lselected.remove( fid );
92
93 emit selectionChanged( lselected, deselected, clearAndSelect );
94}
95
97
98QgsRelationEditorWidget::QgsRelationEditorWidget( const QVariantMap &config, QWidget *parent )
100 , mButtonsVisibility( qgsFlagKeysToValue( config.value( u"buttons"_s ).toString(), QgsRelationEditorWidget::Button::AllButtons ) )
101 , mShowFirstFeature( config.value( u"show_first_feature"_s, true ).toBool() )
102 , mAllowAddChildFeatureWithNoGeometry( config.value( u"allow_add_child_feature_with_no_geometry"_s, false ).toBool() )
103 , mFilterExpression( config.value( u"filter_expression"_s ).toString() )
104{
105 QVBoxLayout *rootLayout = new QVBoxLayout( this );
106 rootLayout->setContentsMargins( 0, 9, 0, 0 );
107
108 // buttons
109 QHBoxLayout *buttonLayout = new QHBoxLayout();
110 buttonLayout->setContentsMargins( 0, 0, 0, 0 );
111 // toggle editing
112 mToggleEditingButton = new QToolButton( this );
113 mToggleEditingButton->setObjectName( u"mToggleEditingButton"_s );
114 mToggleEditingButton->setIcon( QgsApplication::getThemeIcon( u"/mActionToggleEditing.svg"_s ) );
115 mToggleEditingButton->setText( tr( "Toggle Editing" ) );
116 mToggleEditingButton->setEnabled( false );
117 mToggleEditingButton->setCheckable( true );
118 mToggleEditingButton->setToolTip( tr( "Toggle editing mode for child layer" ) );
119 buttonLayout->addWidget( mToggleEditingButton );
120 // save Edits
121 mSaveEditsButton = new QToolButton( this );
122 mSaveEditsButton->setIcon( QgsApplication::getThemeIcon( u"/mActionSaveEdits.svg"_s ) );
123 mSaveEditsButton->setText( tr( "Save Child Layer Edits" ) );
124 mSaveEditsButton->setToolTip( tr( "Save child layer edits" ) );
125 mSaveEditsButton->setEnabled( true );
126 buttonLayout->addWidget( mSaveEditsButton );
127 // add feature with geometry
128 mAddFeatureGeometryButton = new QToolButton( this );
129 mAddFeatureGeometryButton->setObjectName( u"mAddFeatureGeometryButton"_s );
130 buttonLayout->addWidget( mAddFeatureGeometryButton );
131 // add feature
132 mAddFeatureButton = new QToolButton( this );
133 mAddFeatureButton->setIcon( QgsApplication::getThemeIcon( u"/mActionNewTableRow.svg"_s ) );
134 mAddFeatureButton->setText( tr( "Add Child Feature" ) );
135 mAddFeatureButton->setToolTip( tr( "Add child feature" ) );
136 mAddFeatureButton->setObjectName( u"mAddFeatureButton"_s );
137 buttonLayout->addWidget( mAddFeatureButton );
138 // duplicate feature
139 mDuplicateFeatureButton = new QToolButton( this );
140 mDuplicateFeatureButton->setIcon( QgsApplication::getThemeIcon( u"/mActionDuplicateFeature.svg"_s ) );
141 mDuplicateFeatureButton->setText( tr( "Duplicate Child Feature(s)" ) );
142 mDuplicateFeatureButton->setToolTip( tr( "Duplicate selected child feature(s)" ) );
143 mDuplicateFeatureButton->setObjectName( u"mDuplicateFeatureButton"_s );
144 buttonLayout->addWidget( mDuplicateFeatureButton );
145 // delete feature
146 mDeleteFeatureButton = new QToolButton( this );
147 mDeleteFeatureButton->setIcon( QgsApplication::getThemeIcon( u"/mActionDeleteSelectedFeatures.svg"_s ) );
148 mDeleteFeatureButton->setText( tr( "Delete Child Feature(s)" ) );
149 mDeleteFeatureButton->setToolTip( tr( "Delete selected child feature(s)" ) );
150 mDeleteFeatureButton->setObjectName( u"mDeleteFeatureButton"_s );
151 buttonLayout->addWidget( mDeleteFeatureButton );
152 // link feature
153 mLinkFeatureButton = new QToolButton( this );
154 mLinkFeatureButton->setIcon( QgsApplication::getThemeIcon( u"/mActionLink.svg"_s ) );
155 mLinkFeatureButton->setText( tr( "Link Existing Feature(s)" ) );
156 mLinkFeatureButton->setToolTip( tr( "Link existing child feature(s)" ) );
157 mLinkFeatureButton->setObjectName( u"mLinkFeatureButton"_s );
158 buttonLayout->addWidget( mLinkFeatureButton );
159 // unlink feature
160 mUnlinkFeatureButton = new QToolButton( this );
161 mUnlinkFeatureButton->setIcon( QgsApplication::getThemeIcon( u"/mActionUnlink.svg"_s ) );
162 mUnlinkFeatureButton->setText( tr( "Unlink Feature(s)" ) );
163 mUnlinkFeatureButton->setToolTip( tr( "Unlink selected child feature(s)" ) );
164 mUnlinkFeatureButton->setObjectName( u"mUnlinkFeatureButton"_s );
165 buttonLayout->addWidget( mUnlinkFeatureButton );
166 // zoom to linked feature
167 mZoomToFeatureButton = new QToolButton( this );
168 mZoomToFeatureButton->setIcon( QgsApplication::getThemeIcon( u"/mActionZoomToSelected.svg"_s ) );
169 mZoomToFeatureButton->setText( tr( "Zoom To Feature(s)" ) );
170 mZoomToFeatureButton->setToolTip( tr( "Zoom to selected child feature(s)" ) );
171 mZoomToFeatureButton->setObjectName( u"mZoomToFeatureButton"_s );
172 buttonLayout->addWidget( mZoomToFeatureButton );
173 // spacer
174 buttonLayout->addItem( new QSpacerItem( 0, 0, QSizePolicy::Expanding ) );
175 // form view
176 mFormViewButton = new QToolButton( this );
177 mFormViewButton->setText( tr( "Form View" ) );
178 mFormViewButton->setToolTip( tr( "Switch to form view" ) );
179 mFormViewButton->setIcon( QgsApplication::getThemeIcon( u"/mActionPropertyItem.svg"_s ) );
180 mFormViewButton->setCheckable( true );
181 mFormViewButton->setChecked( mViewMode == QgsDualView::AttributeEditor );
182 buttonLayout->addWidget( mFormViewButton );
183 // table view
184 mTableViewButton = new QToolButton( this );
185 mTableViewButton->setText( tr( "Table View" ) );
186 mTableViewButton->setToolTip( tr( "Switch to table view" ) );
187 mTableViewButton->setIcon( QgsApplication::getThemeIcon( u"/mActionOpenTable.svg"_s ) );
188 mTableViewButton->setCheckable( true );
189 mTableViewButton->setChecked( mViewMode == QgsDualView::AttributeTable );
190 buttonLayout->addWidget( mTableViewButton );
191 // button group
192 mViewModeButtonGroup = new QButtonGroup( this );
193 mViewModeButtonGroup->addButton( mFormViewButton, QgsDualView::AttributeEditor );
194 mViewModeButtonGroup->addButton( mTableViewButton, QgsDualView::AttributeTable );
195 // multiedit info label
196 mMultiEditInfoLabel = new QLabel( this );
197 buttonLayout->addWidget( mMultiEditInfoLabel );
198
199 // add buttons layout
200 rootLayout->addLayout( buttonLayout );
201
202 // add stacked widget
203 mStackedWidget = new QStackedWidget( this );
204
205 // add dual view (single feature content)
206 mDualView = new QgsDualView( this );
207 mDualView->setView( mViewMode );
208 connect( mDualView, &QgsDualView::showContextMenuExternally, this, &QgsRelationEditorWidget::showContextMenu );
209
210 // add multi feature editing page
211 mMultiEditStackedWidgetPage = new QWidget( this );
212 {
213 QVBoxLayout *vBoxLayout = new QVBoxLayout();
214 vBoxLayout->setContentsMargins( 0, 0, 0, 0 );
215
216 mMultiEditTreeWidget = new QTreeWidget( this );
217 mMultiEditTreeWidget->setHeaderHidden( true );
218 mMultiEditTreeWidget->setSelectionMode( QTreeWidget::ExtendedSelection );
219 vBoxLayout->addWidget( mMultiEditTreeWidget );
220
221 mMultiEditStackedWidgetPage->setLayout( vBoxLayout );
222 }
223 mStackedWidget->addWidget( mMultiEditStackedWidgetPage );
224
225 mStackedWidget->addWidget( mDualView );
226
227 rootLayout->addWidget( mStackedWidget );
228
229 connect( mViewModeButtonGroup, &QButtonGroup::idClicked, this, static_cast<void ( QgsRelationEditorWidget::* )( int )>( &QgsRelationEditorWidget::setViewMode ) );
230 connect( mToggleEditingButton, &QAbstractButton::clicked, this, &QgsRelationEditorWidget::toggleEditing );
231 connect( mSaveEditsButton, &QAbstractButton::clicked, this, &QgsRelationEditorWidget::saveEdits );
232 connect( mAddFeatureButton, &QAbstractButton::clicked, this, &QgsRelationEditorWidget::addFeature );
233 connect( mAddFeatureGeometryButton, &QAbstractButton::clicked, this, &QgsRelationEditorWidget::addFeatureGeometry );
234 connect( mDuplicateFeatureButton, &QAbstractButton::clicked, this, &QgsRelationEditorWidget::duplicateSelectedFeatures );
235 connect( mDeleteFeatureButton, &QAbstractButton::clicked, this, &QgsRelationEditorWidget::deleteSelectedFeatures );
236 connect( mLinkFeatureButton, &QAbstractButton::clicked, this, &QgsRelationEditorWidget::linkFeature );
237 connect( mUnlinkFeatureButton, &QAbstractButton::clicked, this, &QgsRelationEditorWidget::unlinkSelectedFeatures );
238 connect( mZoomToFeatureButton, &QAbstractButton::clicked, this, &QgsRelationEditorWidget::zoomToSelectedFeatures );
239 connect( mMultiEditTreeWidget, &QTreeWidget::itemSelectionChanged, this, &QgsRelationEditorWidget::multiEditItemSelectionChanged );
240
241 // Set initial state for add/remove etc. buttons
242 updateButtons();
243
244 setLayout( rootLayout );
245}
246
247void QgsRelationEditorWidget::initDualView( QgsVectorLayer *layer, const QgsFeatureRequest &request )
248{
249 if ( multiEditModeActive() )
250 {
251 QgsLogger::warning( tr( "Dual view should not be used in multiple edit mode" ) );
252 return;
253 }
254
255 QgsAttributeEditorContext ctx { mEditorContext };
256 ctx.setParentFormFeature( mFeatureList.first() );
257 mDualView->init( layer, mEditorContext.mapCanvas(), request, ctx, true, mShowFirstFeature );
258 mFeatureSelectionMgr = new QgsFilteredSelectionManager( layer, request, mDualView );
259 mDualView->setFeatureSelectionManager( mFeatureSelectionMgr );
260
261 connect( mFeatureSelectionMgr, &QgsIFeatureSelectionManager::selectionChanged, this, &QgsRelationEditorWidget::updateButtons );
262
263 QIcon icon;
264 QString text;
265 if ( layer->geometryType() == Qgis::GeometryType::Point )
266 {
267 icon = QgsApplication::getThemeIcon( u"/mActionCapturePoint.svg"_s );
268 text = tr( "Add Point Child Feature" );
269 }
270 else if ( layer->geometryType() == Qgis::GeometryType::Line )
271 {
272 icon = QgsApplication::getThemeIcon( u"/mActionCaptureLine.svg"_s );
273 text = tr( "Add Line Child Feature" );
274 }
275 else if ( layer->geometryType() == Qgis::GeometryType::Polygon )
276 {
277 icon = QgsApplication::getThemeIcon( u"/mActionCapturePolygon.svg"_s );
278 text = tr( "Add Polygon Child Feature" );
279 }
280
281 mAddFeatureGeometryButton->setIcon( icon );
282 mAddFeatureGeometryButton->setText( text );
283 mAddFeatureGeometryButton->setToolTip( text );
284
285 updateButtons();
286}
287
289{
290 mEditorContext = context;
291
292 if ( context.mapCanvas() && context.cadDockWidget() )
293 {
294 mMapToolDigitize.reset( new QgsMapToolDigitizeFeature( context.mapCanvas(), context.cadDockWidget() ) );
295 mMapToolDigitize->setButton( mAddFeatureGeometryButton );
296 }
297
298 updateButtons();
299}
300
302{
303 mDualView->setView( mode );
304 mViewMode = mode;
305}
306
307void QgsRelationEditorWidget::updateButtons()
308{
309 bool toggleEditingButtonEnabled = false;
310 bool canAdd = false;
311 bool canAddGeometry = false;
312 bool canRemove = false;
313 bool canEdit = false;
314 bool canLink = false;
315 bool canUnlink = false;
316 bool spatial = false;
317
318 if ( mRelation.isValid() )
319 {
320 toggleEditingButtonEnabled = mRelation.referencingLayer()->supportsEditing();
322 canAddGeometry = mRelation.referencingLayer()->isEditable();
323 canRemove = mRelation.referencingLayer()->isEditable();
326 canUnlink = mRelation.referencingLayer()->isEditable();
327 spatial = mRelation.referencingLayer()->isSpatial();
328 }
329
330 if ( mNmRelation.isValid() )
331 {
332 toggleEditingButtonEnabled |= mNmRelation.referencedLayer()->supportsEditing();
333 canAdd = mNmRelation.referencedLayer()->isEditable();
334 canAddGeometry = mNmRelation.referencedLayer()->isEditable();
335 canRemove = mNmRelation.referencedLayer()->isEditable();
336 canEdit = mNmRelation.referencedLayer()->isEditable();
337 spatial = mNmRelation.referencedLayer()->isSpatial();
338 }
339
340 const bool selectionNotEmpty = mFeatureSelectionMgr ? mFeatureSelectionMgr->selectedFeatureCount() : false;
341 if ( multiEditModeActive() )
342 {
343 const bool multieditLinkedChildSelected = !selectedChildFeatureIds().isEmpty();
344
345 canAddGeometry = false;
346
347 canRemove = canRemove && multieditLinkedChildSelected;
348
349 // In 1:n relations an element can be linked only to 1 feature
350 canLink = canLink && mNmRelation.isValid();
351 canUnlink = canUnlink && multieditLinkedChildSelected;
352 }
353 else
354 {
355 canRemove = canRemove && selectionNotEmpty;
356 canUnlink = canUnlink && selectionNotEmpty;
357 }
358
359 mToggleEditingButton->setEnabled( toggleEditingButtonEnabled );
360 mAddFeatureButton->setEnabled( canAdd );
361 mAddFeatureGeometryButton->setEnabled( canAddGeometry );
362 mDuplicateFeatureButton->setEnabled( canEdit && selectionNotEmpty );
363 mLinkFeatureButton->setEnabled( canLink );
364 mDeleteFeatureButton->setEnabled( canRemove );
365 mUnlinkFeatureButton->setEnabled( canUnlink );
366 mZoomToFeatureButton->setEnabled( selectionNotEmpty );
367 mToggleEditingButton->setChecked( canEdit );
368 mSaveEditsButton->setEnabled( canEdit || canLink || canUnlink );
369
370 mToggleEditingButton->setVisible( !mLayerInSameTransactionGroup );
371
372 mLinkFeatureButton->setVisible( mButtonsVisibility.testFlag( QgsRelationEditorWidget::Button::Link ) );
373 mUnlinkFeatureButton->setVisible( mButtonsVisibility.testFlag( QgsRelationEditorWidget::Button::Unlink ) );
374 mSaveEditsButton->setVisible( mButtonsVisibility.testFlag( QgsRelationEditorWidget::Button::SaveChildEdits ) && !mLayerInSameTransactionGroup );
375 mAddFeatureButton->setVisible( mButtonsVisibility.testFlag( QgsRelationEditorWidget::Button::AddChildFeature ) && !( spatial && !mAllowAddChildFeatureWithNoGeometry ) );
376 mAddFeatureGeometryButton->setVisible( mButtonsVisibility.testFlag( QgsRelationEditorWidget::Button::AddChildFeature ) && mEditorContext.mapCanvas() && mEditorContext.cadDockWidget() && spatial );
377 mDuplicateFeatureButton->setVisible( mButtonsVisibility.testFlag( QgsRelationEditorWidget::Button::DuplicateChildFeature ) );
378 mDeleteFeatureButton->setVisible( mButtonsVisibility.testFlag( QgsRelationEditorWidget::Button::DeleteChildFeature ) );
379 mZoomToFeatureButton->setVisible( mButtonsVisibility.testFlag( QgsRelationEditorWidget::Button::ZoomToChildFeature ) && mEditorContext.mapCanvas() && spatial );
380}
381
382void QgsRelationEditorWidget::addFeature()
383{
385
386 if ( !multiEditModeActive() )
387 return;
388
389 mMultiEditTreeWidget->blockSignals( true );
390 mMultiEdit1NJustAddedIds = addedFeatures;
391 QTreeWidgetItemIterator treeWidgetItemIterator( mMultiEditTreeWidget );
392 while ( *treeWidgetItemIterator )
393 {
394 if ( ( *treeWidgetItemIterator )->data( 0, static_cast<int>( MultiEditTreeWidgetRole::FeatureType ) ).toInt() != static_cast<int>( MultiEditFeatureType::Child ) )
395 {
396 ++treeWidgetItemIterator;
397 continue;
398 }
399
400 if ( addedFeatures.contains( ( *treeWidgetItemIterator )->data( 0, static_cast<int>( MultiEditTreeWidgetRole::FeatureId ) ).toInt() ) )
401 ( *treeWidgetItemIterator )->setSelected( true );
402
403 ++treeWidgetItemIterator;
404 }
405 mMultiEditTreeWidget->blockSignals( false );
406
407 updateUi();
408 updateButtons();
409}
410
411void QgsRelationEditorWidget::addFeatureGeometry()
412{
413 if ( multiEditModeActive() )
414 {
415 QgsLogger::warning( tr( "Adding a geometry feature is not supported in multiple edit mode" ) );
416 return;
417 }
418
419 QgsVectorLayer *layer = nullptr;
420 if ( mNmRelation.isValid() )
421 layer = mNmRelation.referencedLayer();
422 else
423 layer = mRelation.referencingLayer();
424
425 mMapToolDigitize->setLayer( layer );
426
427 // window is always on top, so we hide it to digitize without seeing it
428 if ( window()->objectName() != "QgisApp"_L1 )
429 {
430 window()->setVisible( false );
431 }
432 setMapTool( mMapToolDigitize );
433
434 connect( mMapToolDigitize, &QgsMapToolDigitizeFeature::digitizingCompleted, this, &QgsRelationEditorWidget::onDigitizingCompleted );
435
436 connect( mMapToolDigitize, &QgsMapToolDigitizeFeature::digitizingCanceled, this, &QgsRelationEditorWidget::onDigitizingCanceled );
437
438 if ( auto *lMainMessageBar = mEditorContext.mainMessageBar() )
439 {
440 const QString displayString = QgsVectorLayerUtils::getFeatureDisplayString( layer, mFeatureList.first() );
441
442 const QString title = tr( "Create child feature for parent %1 \"%2\"" ).arg( mRelation.referencedLayer()->name(), displayString );
443 const QString msg = tr( "Digitize the geometry for the new feature on layer %1. Press &lt;ESC&gt; to cancel." )
444 .arg( layer->name() );
445 mMessageBarItem = QgsMessageBar::createMessage( title, msg, this );
446 lMainMessageBar->pushItem( mMessageBarItem );
447 }
448}
449
450void QgsRelationEditorWidget::onDigitizingCompleted( const QgsFeature &feature )
451{
453 digitizingFinished();
454}
455
456void QgsRelationEditorWidget::multiEditItemSelectionChanged()
457{
458 const QList<QTreeWidgetItem *> selectedItems = mMultiEditTreeWidget->selectedItems();
459
460 // Select all items pointing to the same feature
461 // but only if we are not deselecting.
462 if ( selectedItems.size() == 1
463 && mMultiEditPreviousSelectedItems.size() <= 1 )
464 {
465 if ( selectedItems.first()->data( 0, static_cast<int>( MultiEditTreeWidgetRole::FeatureType ) ).toInt() == static_cast<int>( MultiEditFeatureType::Child ) )
466 {
467 mMultiEditTreeWidget->blockSignals( true );
468
469 const QgsFeatureId featureIdSelectedItem = selectedItems.first()->data( 0, static_cast<int>( MultiEditTreeWidgetRole::FeatureId ) ).toInt();
470
471 QTreeWidgetItemIterator treeWidgetItemIterator( mMultiEditTreeWidget );
472 while ( *treeWidgetItemIterator )
473 {
474 if ( ( *treeWidgetItemIterator )->data( 0, static_cast<int>( MultiEditTreeWidgetRole::FeatureType ) ).toInt() != static_cast<int>( MultiEditFeatureType::Child ) )
475 {
476 ++treeWidgetItemIterator;
477 continue;
478 }
479
480 const QgsFeatureId featureIdCurrentItem = ( *treeWidgetItemIterator )->data( 0, static_cast<int>( MultiEditTreeWidgetRole::FeatureId ) ).toInt();
481 if ( mNmRelation.isValid() )
482 {
483 if ( featureIdSelectedItem == featureIdCurrentItem )
484 ( *treeWidgetItemIterator )->setSelected( true );
485 }
486 else
487 {
488 if ( !mMultiEdit1NJustAddedIds.contains( featureIdSelectedItem ) )
489 break;
490
491 if ( mMultiEdit1NJustAddedIds.contains( featureIdCurrentItem ) )
492 ( *treeWidgetItemIterator )->setSelected( true );
493 }
494
495 ++treeWidgetItemIterator;
496 }
497 mMultiEditTreeWidget->blockSignals( false );
498 }
499 }
500 mMultiEditPreviousSelectedItems = selectedItems;
501 updateButtons();
502}
503
504void QgsRelationEditorWidget::linkFeature()
505{
507}
508
509void QgsRelationEditorWidget::toggleEditing( bool state ) // cppcheck-suppress duplInheritedMember
510{
512
513 updateButtons();
514}
515
517{
518 if ( !mRelation.isValid() || mFeatureList.isEmpty() || !mFeatureList.first().isValid() )
519 return;
520
521 if ( !isVisible() )
522 return;
523
524 if ( multiEditModeActive() )
525 updateUiMultiEdit();
526 else
527 updateUiSingleEdit();
528}
529
531{
532 mButtonsVisibility = buttons;
533 updateButtons();
534}
535
537{
538 Buttons buttons;
539 if ( mLinkFeatureButton->isVisible() )
540 buttons |= Button::Link;
541 if ( mUnlinkFeatureButton->isVisible() )
542 buttons |= Button::Unlink;
543 if ( mSaveEditsButton->isVisible() )
544 buttons |= Button::SaveChildEdits;
545 if ( mAddFeatureButton->isVisible() )
546 buttons |= Button::AddChildFeature;
547 if ( mDuplicateFeatureButton->isVisible() )
549 if ( mDeleteFeatureButton->isVisible() )
551 if ( mZoomToFeatureButton->isVisible() )
553 return buttons;
554}
555
556void QgsRelationEditorWidget::parentFormValueChanged( const QString &attribute, const QVariant &newValue )
557{
558 mDualView->parentFormValueChanged( attribute, newValue );
559}
560
561void QgsRelationEditorWidget::showContextMenu( QgsActionMenu *menu, const QgsFeatureId fid )
562{
564 {
565 QAction *qAction = nullptr;
566
567 if ( mButtonsVisibility.testFlag( QgsRelationEditorWidget::Button::DeleteChildFeature ) )
568 {
569 qAction = menu->addAction( QgsApplication::getThemeIcon( u"/mActionDeleteSelected.svg"_s ), tr( "Delete Feature" ) );
570 connect( qAction, &QAction::triggered, this, [this, fid]() { deleteFeature( fid ); } );
571 }
572
573 if ( mButtonsVisibility.testFlag( QgsRelationEditorWidget::Button::Unlink ) )
574 {
575 qAction = menu->addAction( QgsApplication::getThemeIcon( u"/mActionUnlink.svg"_s ), tr( "Unlink Feature" ) );
576 connect( qAction, &QAction::triggered, this, [this, fid]() { unlinkFeature( fid ); } );
577 }
578 }
579}
580
581void QgsRelationEditorWidget::setMapTool( QgsMapTool *mapTool )
582{
583 QgsMapCanvas *mapCanvas = mEditorContext.mapCanvas();
584
585 mapCanvas->setMapTool( mapTool );
586 mapCanvas->window()->raise();
587 mapCanvas->activateWindow();
588 mapCanvas->setFocus();
589 connect( mapTool, &QgsMapTool::deactivated, this, &QgsRelationEditorWidget::mapToolDeactivated );
590}
591
592void QgsRelationEditorWidget::unsetMapTool()
593{
594 QgsMapCanvas *mapCanvas = mEditorContext.mapCanvas();
595
596 // this will call mapToolDeactivated
597 mapCanvas->unsetMapTool( mMapToolDigitize );
598
599 disconnect( mMapToolDigitize, &QgsMapToolDigitizeFeature::digitizingCompleted, this, &QgsRelationEditorWidget::onDigitizingCompleted );
600}
601
602QgsFeatureIds QgsRelationEditorWidget::selectedChildFeatureIds() const
603{
604 if ( multiEditModeActive() )
605 {
606 QgsFeatureIds featureIds;
607 for ( QTreeWidgetItem *treeWidgetItem : mMultiEditTreeWidget->selectedItems() )
608 {
609 if ( static_cast<MultiEditFeatureType>( treeWidgetItem->data( 0, static_cast<int>( MultiEditTreeWidgetRole::FeatureType ) ).toInt() ) != MultiEditFeatureType::Child )
610 continue;
611
612 featureIds.insert( treeWidgetItem->data( 0, static_cast<int>( MultiEditTreeWidgetRole::FeatureId ) ).toLongLong() );
613 }
614 return featureIds;
615 }
616 else if ( mFeatureSelectionMgr )
617 {
618 return mFeatureSelectionMgr->selectedFeatureIds();
619 }
620 else
621 {
622 return {};
623 }
624}
625
626void QgsRelationEditorWidget::updateUiSingleEdit()
627{
628 mFormViewButton->setVisible( true );
629 mTableViewButton->setVisible( true );
630 mMultiEditInfoLabel->setVisible( false );
631
632 mStackedWidget->setCurrentWidget( mDualView );
633
634 QgsFeatureRequest request = mRelation.getRelatedFeaturesRequest( mFeatureList.first() );
635 QgsVectorLayer *layer = nullptr;
636 if ( mNmRelation.isValid() )
637 {
638 QgsFeature fet;
639 QgsFeatureRequest nmRequest;
640
641 QgsFeatureIterator it = mRelation.referencingLayer()->getFeatures( request );
642 QStringList filters;
643
644 while ( it.nextFeature( fet ) )
645 {
646 QString filter = mNmRelation.getReferencedFeatureRequest( fet ).filterExpression()->expression();
647 filters << filter.prepend( '(' ).append( ')' );
648 }
649
650 QString reducedExpression;
651 if ( QgsExpression::attemptReduceToInClause( filters, reducedExpression ) )
652 {
653 nmRequest.setFilterExpression( reducedExpression );
654 }
655 else
656 {
657 nmRequest.setFilterExpression( filters.join( " OR "_L1 ) );
658 }
659
660 request = std::move( nmRequest );
661 layer = mNmRelation.referencedLayer();
662 }
663 else if ( mRelation.referencingLayer() )
664 {
665 layer = mRelation.referencingLayer();
666 }
667
668 if ( !layer )
669 return;
670
671 // don't recreate all the widget from scratch if only the request has changed
672 if ( !mDualView->masterModel() || layer != mDualView->masterModel()->layer() )
673 {
674 initDualView( layer, request );
675 }
676 else
677 {
678 mFeatureSelectionMgr = new QgsFilteredSelectionManager( layer, request, mDualView );
679 mDualView->setFeatureSelectionManager( mFeatureSelectionMgr );
680 connect( mFeatureSelectionMgr, &QgsIFeatureSelectionManager::selectionChanged, this, &QgsRelationEditorWidget::updateButtons );
681
682 mDualView->setRequest( request );
683 mDualView->masterModel()->loadLayer();
684
685 updateButtons();
686 }
687}
688
689void QgsRelationEditorWidget::updateUiMultiEdit()
690{
691 mFormViewButton->setVisible( false );
692 mTableViewButton->setVisible( false );
693 mMultiEditInfoLabel->setVisible( true );
694
695 mStackedWidget->setCurrentWidget( mMultiEditStackedWidgetPage );
696
697 QList<QTreeWidgetItem *> parentTreeWidgetItems;
698
699 QgsFeatureIds featureIdsMixedValues;
700 QMultiMap<QTreeWidgetItem *, QgsFeatureId> multimapChildFeatures;
701
702 mMultiEditTreeWidget->clear();
703 for ( const QgsFeature &featureParent : std::as_const( mFeatureList ) )
704 {
705 QTreeWidgetItem *treeWidgetItem = createMultiEditTreeWidgetItem( featureParent, mRelation.referencedLayer(), MultiEditFeatureType::Parent );
706
707 // Parent feature items are not selectable
708 treeWidgetItem->setFlags( Qt::ItemIsEnabled );
709
710 parentTreeWidgetItems.append( treeWidgetItem );
711
712 // Get child features
713 const QgsFeatureRequest request = relation().getRelatedFeaturesRequest( featureParent );
714 QgsFeatureIterator featureIterator = mRelation.referencingLayer()->getFeatures( request );
715 QgsFeature featureChild;
716 while ( featureIterator.nextFeature( featureChild ) )
717 {
718 if ( mNmRelation.isValid() )
719 {
720 const QgsFeatureRequest requestFinalChild = mNmRelation.getReferencedFeatureRequest( featureChild );
721 QgsFeatureIterator featureIteratorFinalChild = mNmRelation.referencedLayer()->getFeatures( requestFinalChild );
722 QgsFeature featureChildChild;
723 while ( featureIteratorFinalChild.nextFeature( featureChildChild ) )
724 {
725 QTreeWidgetItem *treeWidgetItemChild = createMultiEditTreeWidgetItem( featureChildChild, mNmRelation.referencedLayer(), MultiEditFeatureType::Child );
726
727 treeWidgetItem->addChild( treeWidgetItemChild );
728
729 featureIdsMixedValues.insert( featureChildChild.id() );
730 multimapChildFeatures.insert( treeWidgetItem, featureChildChild.id() );
731 }
732 }
733 else
734 {
735 QTreeWidgetItem *treeWidgetItemChild = createMultiEditTreeWidgetItem( featureChild, mRelation.referencingLayer(), MultiEditFeatureType::Child );
736 treeWidgetItem->addChild( treeWidgetItemChild );
737
738 featureIdsMixedValues.insert( featureChild.id() );
739 }
740 }
741
742 mMultiEditTreeWidget->addTopLevelItem( treeWidgetItem );
743 treeWidgetItem->setExpanded( true );
744 }
745
746 // Set mixed values indicator (Green or Orange)
747 //
748 // Green:
749 // n:m and 1:n: 0 child features available
750 // n:m with no mixed values
751 // Orange:
752 // n:m with mixed values
753 // 1:n always, including when we pseudo know that feature are related (just added feature)
754 //
755 // See https://github.com/qgis/QGIS/pull/45703
756 //
757 if ( mNmRelation.isValid() )
758 {
759 QgsFeatureIds::iterator iterator = featureIdsMixedValues.begin();
760 while ( iterator != featureIdsMixedValues.end() )
761 {
762 bool mixedValues = false;
763 for ( QTreeWidgetItem *parentTreeWidgetItem : parentTreeWidgetItems )
764 {
765 if ( !multimapChildFeatures.values( parentTreeWidgetItem ).contains( *iterator ) )
766 {
767 mixedValues = true;
768 break;
769 }
770 }
771
772 if ( !mixedValues )
773 {
774 iterator = featureIdsMixedValues.erase( iterator );
775 continue;
776 }
777
778 ++iterator;
779 }
780 }
781
782 // Set multiedit info label
783 if ( featureIdsMixedValues.isEmpty() )
784 {
785 QIcon icon = QgsApplication::getThemeIcon( u"/multieditSameValues.svg"_s );
786 mMultiEditInfoLabel->setPixmap( icon.pixmap( mMultiEditInfoLabel->height(), mMultiEditInfoLabel->height() ) );
787 mMultiEditInfoLabel->setToolTip( tr( "All features in selection have equal relations" ) );
788 }
789 else
790 {
791 QIcon icon = QgsApplication::getThemeIcon( u"/multieditMixedValues.svg"_s );
792 mMultiEditInfoLabel->setPixmap( icon.pixmap( mMultiEditInfoLabel->height(), mMultiEditInfoLabel->height() ) );
793 mMultiEditInfoLabel->setToolTip( tr( "Some features in selection have different relations" ) );
794
795 // Set italic font for mixed values
796 QFont fontItalic = mMultiEditTreeWidget->font();
797 fontItalic.setItalic( true );
798 for ( QTreeWidgetItem *parentTreeWidgetItem : parentTreeWidgetItems )
799 {
800 for ( int childIndex = 0; childIndex < parentTreeWidgetItem->childCount(); ++childIndex )
801 {
802 QTreeWidgetItem *childItem = parentTreeWidgetItem->child( childIndex );
803 const QgsFeatureId featureIdCurrentItem = childItem->data( 0, static_cast<int>( MultiEditTreeWidgetRole::FeatureId ) ).toInt();
804 if ( featureIdsMixedValues.contains( featureIdCurrentItem ) )
805 childItem->setFont( 0, fontItalic );
806 }
807 }
808 }
809}
810
811QTreeWidgetItem *QgsRelationEditorWidget::createMultiEditTreeWidgetItem( const QgsFeature &feature, QgsVectorLayer *layer, MultiEditFeatureType type )
812{
813 QTreeWidgetItem *treeWidgetItem = new QTreeWidgetItem();
814 treeWidgetItem->setText( 0, QgsVectorLayerUtils::getFeatureDisplayString( layer, feature ) );
815 treeWidgetItem->setIcon( 0, QgsIconUtils::iconForLayer( layer ) );
816 treeWidgetItem->setData( 0, static_cast<int>( MultiEditTreeWidgetRole::FeatureType ), static_cast<int>( type ) );
817 treeWidgetItem->setData( 0, static_cast<int>( MultiEditTreeWidgetRole::FeatureId ), feature.id() );
818 return treeWidgetItem;
819}
820
821void QgsRelationEditorWidget::onDigitizingCanceled()
822{
823 digitizingFinished();
824}
825
826void QgsRelationEditorWidget::digitizingFinished()
827{
828 window()->setVisible( true );
829 window()->raise();
830 window()->activateWindow();
831 unsetMapTool();
832}
833
834void QgsRelationEditorWidget::mapToolDeactivated()
835{
836 if ( mEditorContext.mainMessageBar() && mMessageBarItem )
837 {
838 mEditorContext.mainMessageBar()->popWidget( mMessageBarItem );
839 }
840 mMessageBarItem = nullptr;
841}
842
844{
845 return QVariantMap( { { "buttons", qgsFlagValueToKeys( visibleButtons() ) }, { "show_first_feature", mShowFirstFeature }, { "allow_add_child_feature_with_no_geometry", mAllowAddChildFeatureWithNoGeometry }, { "filter_expression", mFilterExpression } } );
846}
847
849{
850 mButtonsVisibility = qgsFlagKeysToValue( config.value( u"buttons"_s ).toString(), QgsRelationEditorWidget::Button::AllButtons );
851 mShowFirstFeature = config.value( u"show_first_feature"_s, true ).toBool();
852 mAllowAddChildFeatureWithNoGeometry = config.value( u"allow_add_child_feature_with_no_geometry"_s, false ).toBool();
853 mFilterExpression = config.value( u"filter_expression"_s ).toString();
854 updateButtons();
855}
856
858{
859 Q_UNUSED( newRelation );
860 Q_UNUSED( newFeature );
861
862 if ( !mRelation.isValid() )
863 return;
864
865 disconnect( mRelation.referencingLayer(), &QgsVectorLayer::editingStarted, this, &QgsRelationEditorWidget::updateButtons );
866 disconnect( mRelation.referencingLayer(), &QgsVectorLayer::editingStopped, this, &QgsRelationEditorWidget::updateButtons );
867}
868
870{
871 if ( !mRelation.isValid()
872 || mFeatureList.isEmpty() )
873 {
874 updateButtons();
875 return;
876 }
877
878 connect( mRelation.referencingLayer(), &QgsVectorLayer::editingStarted, this, &QgsRelationEditorWidget::updateButtons );
879 connect( mRelation.referencingLayer(), &QgsVectorLayer::editingStopped, this, &QgsRelationEditorWidget::updateButtons );
880
881 updateButtons();
882
883 const QgsFeatureRequest myRequest = mRelation.getRelatedFeaturesRequest( mFeatureList.first() );
884 initDualView( mRelation.referencingLayer(), myRequest );
885}
886
887void QgsRelationEditorWidget::beforeSetRelations( const QgsRelation &newRelation, const QgsRelation &newNmRelation )
888{
889 Q_UNUSED( newRelation );
890 Q_UNUSED( newNmRelation );
891
892 if ( mRelation.isValid() )
893 {
894 disconnect( mRelation.referencingLayer(), &QgsVectorLayer::editingStarted, this, &QgsRelationEditorWidget::updateButtons );
895 disconnect( mRelation.referencingLayer(), &QgsVectorLayer::editingStopped, this, &QgsRelationEditorWidget::updateButtons );
896 }
897
898 if ( mNmRelation.isValid() )
899 {
900 disconnect( mNmRelation.referencedLayer(), &QgsVectorLayer::editingStarted, this, &QgsRelationEditorWidget::updateButtons );
901 disconnect( mNmRelation.referencedLayer(), &QgsVectorLayer::editingStopped, this, &QgsRelationEditorWidget::updateButtons );
902 }
903}
904
906{
907 if ( !mRelation.isValid() )
908 return;
909
910 connect( mRelation.referencingLayer(), &QgsVectorLayer::editingStarted, this, &QgsRelationEditorWidget::updateButtons );
911 connect( mRelation.referencingLayer(), &QgsVectorLayer::editingStopped, this, &QgsRelationEditorWidget::updateButtons );
912
913 if ( mNmRelation.isValid() )
914 {
915 connect( mNmRelation.referencedLayer(), &QgsVectorLayer::editingStarted, this, &QgsRelationEditorWidget::updateButtons );
916 connect( mNmRelation.referencedLayer(), &QgsVectorLayer::editingStopped, this, &QgsRelationEditorWidget::updateButtons );
917 }
918
919 updateButtons();
920}
921
926
928{
929 const QgsFeatureIds selectedFids = selectedChildFeatureIds();
930 unlinkFeatures( selectedFids );
931}
932
934{
935 duplicateFeatures( mFeatureSelectionMgr->selectedFeatureIds() );
936}
937
939{
940 duplicateFeatures( mFeatureSelectionMgr->selectedFeatureIds() );
941}
942
944{
945 const QgsFeatureIds selectedFids = selectedChildFeatureIds();
946 deleteFeatures( selectedFids );
947}
948
950{
951 QgsMapCanvas *c = mEditorContext.mapCanvas();
952 if ( !c )
953 return;
954
955 c->zoomToFeatureIds(
956 mNmRelation.isValid() ? mNmRelation.referencedLayer() : mRelation.referencingLayer(),
957 mFeatureSelectionMgr->selectedFeatureIds()
958 );
959}
960
962
963
966{
967 setupUi( this );
968 connect( mEditExpression, &QAbstractButton::clicked, this, &QgsRelationEditorConfigWidget::mEditExpression_clicked );
969
970 // Make filter depending on link button
971 filterExpressionLabel->setEnabled( mRelationShowLinkCheckBox->isChecked() );
972 mEditExpression->setEnabled( mRelationShowLinkCheckBox->isChecked() );
973 mFilterExpression->setEnabled( mRelationShowLinkCheckBox->isChecked() );
974 connect( mRelationShowLinkCheckBox, &QCheckBox::toggled, filterExpressionLabel, &QLabel::setEnabled );
975 connect( mRelationShowLinkCheckBox, &QCheckBox::toggled, mEditExpression, &QToolButton::setEnabled );
976 connect( mRelationShowLinkCheckBox, &QCheckBox::toggled, mFilterExpression, &QTextEdit::setEnabled );
977
978 // Make add feature with no geometry depending on add button
979 mAllowAddChildFeatureWithNoGeometry->setEnabled( mRelationShowAddChildCheckBox->isChecked() );
980 connect( mRelationShowAddChildCheckBox, &QCheckBox::toggled, mAllowAddChildFeatureWithNoGeometry, &QCheckBox::setEnabled );
981}
982
984{
985 QgsVectorLayer *vl = nullptr;
986
987 if ( nmRelation().isValid() )
988 {
990 }
991 else
992 {
993 vl = relation().referencingLayer();
994 }
995
996 // Show expression builder
998 QgsExpressionBuilderDialog dlg( vl, mFilterExpression->toPlainText(), this, u"generic"_s, context );
999 dlg.setWindowTitle( tr( "Edit Filter Expression of Target Layer" ) );
1000
1001 if ( dlg.exec() == QDialog::Accepted )
1002 {
1003 mFilterExpression->setPlainText( dlg.expressionBuilder()->expressionText() );
1004 }
1005}
1006
1008{
1010 buttons.setFlag( QgsRelationEditorWidget::Button::Link, mRelationShowLinkCheckBox->isChecked() );
1011 buttons.setFlag( QgsRelationEditorWidget::Button::Unlink, mRelationShowUnlinkCheckBox->isChecked() );
1012 buttons.setFlag( QgsRelationEditorWidget::Button::AddChildFeature, mRelationShowAddChildCheckBox->isChecked() );
1013 buttons.setFlag( QgsRelationEditorWidget::Button::DuplicateChildFeature, mRelationShowDuplicateChildFeatureCheckBox->isChecked() );
1014 buttons.setFlag( QgsRelationEditorWidget::Button::ZoomToChildFeature, mRelationShowZoomToFeatureCheckBox->isChecked() );
1015 buttons.setFlag( QgsRelationEditorWidget::Button::DeleteChildFeature, mRelationDeleteChildFeatureCheckBox->isChecked() );
1016 buttons.setFlag( QgsRelationEditorWidget::Button::SaveChildEdits, mRelationShowSaveChildEditsCheckBox->isChecked() );
1017
1018 return QVariantMap(
1019 { { "buttons", qgsFlagValueToKeys( buttons ) },
1020 { "show_first_feature", mShowFirstFeature->isChecked() },
1021 { "allow_add_child_feature_with_no_geometry", mAllowAddChildFeatureWithNoGeometry->isChecked() },
1022 { "filter_expression", mFilterExpression->toPlainText() }
1023 }
1024 );
1025}
1026
1028{
1030
1031 mRelationShowLinkCheckBox->setChecked( buttons.testFlag( QgsRelationEditorWidget::Button::Link ) );
1032 mRelationShowUnlinkCheckBox->setChecked( buttons.testFlag( QgsRelationEditorWidget::Button::Unlink ) );
1033 mRelationShowAddChildCheckBox->setChecked( buttons.testFlag( QgsRelationEditorWidget::Button::AddChildFeature ) );
1034 mRelationShowDuplicateChildFeatureCheckBox->setChecked( buttons.testFlag( QgsRelationEditorWidget::Button::DuplicateChildFeature ) );
1035 mRelationShowZoomToFeatureCheckBox->setChecked( buttons.testFlag( QgsRelationEditorWidget::Button::ZoomToChildFeature ) );
1036 mRelationDeleteChildFeatureCheckBox->setChecked( buttons.testFlag( QgsRelationEditorWidget::Button::DeleteChildFeature ) );
1037 mRelationShowSaveChildEditsCheckBox->setChecked( buttons.testFlag( QgsRelationEditorWidget::Button::SaveChildEdits ) );
1038 mShowFirstFeature->setChecked( config.value( u"show_first_feature"_s, true ).toBool() );
1039 mAllowAddChildFeatureWithNoGeometry->setChecked( config.value( u"allow_add_child_feature_with_no_geometry"_s, false ).toBool() );
1040 mFilterExpression->setPlainText( config.value( u"filter_expression"_s ).toString() );
1041}
1042
1043
1045
1046
1047#ifndef SIP_RUN
1051
1053{
1054 return u"relation_editor"_s;
1055}
1056
1058{
1059 return QObject::tr( "Relation Editor" );
1060}
1061
1062QgsAbstractRelationEditorWidget *QgsRelationEditorWidgetFactory::create( const QVariantMap &config, QWidget *parent ) const
1063{
1064 return new QgsRelationEditorWidget( config, parent );
1065}
1066
1068{
1069 return static_cast<QgsAbstractRelationEditorConfigWidget *>( new QgsRelationEditorConfigWidget( relation, parent ) );
1070}
1071#endif
@ Point
Points.
Definition qgis.h:366
@ Line
Lines.
Definition qgis.h:367
@ Polygon
Polygons.
Definition qgis.h:368
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:60
QgsFeatureId id
Definition qgsfeature.h:68
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:87
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:7149
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:7171
QSet< QgsFeatureId > QgsFeatureIds
qint64 QgsFeatureId
64 bit feature ids negative numbers are used for uncommitted/newly added features