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