QGIS API Documentation 4.1.0-Master (5bf3c20f3c9)
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." ).arg( layer->name() );
444 mMessageBarItem = QgsMessageBar::createMessage( title, msg, this );
445 lMainMessageBar->pushItem( mMessageBarItem );
446 }
447}
448
449void QgsRelationEditorWidget::onDigitizingCompleted( const QgsFeature &feature )
450{
452 digitizingFinished();
453}
454
455void QgsRelationEditorWidget::multiEditItemSelectionChanged()
456{
457 const QList<QTreeWidgetItem *> selectedItems = mMultiEditTreeWidget->selectedItems();
458
459 // Select all items pointing to the same feature
460 // but only if we are not deselecting.
461 if ( selectedItems.size() == 1 && mMultiEditPreviousSelectedItems.size() <= 1 )
462 {
463 if ( selectedItems.first()->data( 0, static_cast<int>( MultiEditTreeWidgetRole::FeatureType ) ).toInt() == static_cast<int>( MultiEditFeatureType::Child ) )
464 {
465 mMultiEditTreeWidget->blockSignals( true );
466
467 const QgsFeatureId featureIdSelectedItem = selectedItems.first()->data( 0, static_cast<int>( MultiEditTreeWidgetRole::FeatureId ) ).toInt();
468
469 QTreeWidgetItemIterator treeWidgetItemIterator( mMultiEditTreeWidget );
470 while ( *treeWidgetItemIterator )
471 {
472 if ( ( *treeWidgetItemIterator )->data( 0, static_cast<int>( MultiEditTreeWidgetRole::FeatureType ) ).toInt() != static_cast<int>( MultiEditFeatureType::Child ) )
473 {
474 ++treeWidgetItemIterator;
475 continue;
476 }
477
478 const QgsFeatureId featureIdCurrentItem = ( *treeWidgetItemIterator )->data( 0, static_cast<int>( MultiEditTreeWidgetRole::FeatureId ) ).toInt();
479 if ( mNmRelation.isValid() )
480 {
481 if ( featureIdSelectedItem == featureIdCurrentItem )
482 ( *treeWidgetItemIterator )->setSelected( true );
483 }
484 else
485 {
486 if ( !mMultiEdit1NJustAddedIds.contains( featureIdSelectedItem ) )
487 break;
488
489 if ( mMultiEdit1NJustAddedIds.contains( featureIdCurrentItem ) )
490 ( *treeWidgetItemIterator )->setSelected( true );
491 }
492
493 ++treeWidgetItemIterator;
494 }
495 mMultiEditTreeWidget->blockSignals( false );
496 }
497 }
498 mMultiEditPreviousSelectedItems = selectedItems;
499 updateButtons();
500}
501
502void QgsRelationEditorWidget::linkFeature()
503{
505}
506
507void QgsRelationEditorWidget::toggleEditing( bool state ) // cppcheck-suppress duplInheritedMember
508{
510
511 updateButtons();
512}
513
515{
516 if ( !mRelation.isValid() || mFeatureList.isEmpty() || !mFeatureList.first().isValid() )
517 return;
518
519 if ( !isVisible() )
520 return;
521
522 if ( multiEditModeActive() )
523 updateUiMultiEdit();
524 else
525 updateUiSingleEdit();
526}
527
529{
530 mButtonsVisibility = buttons;
531 updateButtons();
532}
533
535{
536 Buttons buttons;
537 if ( mLinkFeatureButton->isVisible() )
538 buttons |= Button::Link;
539 if ( mUnlinkFeatureButton->isVisible() )
540 buttons |= Button::Unlink;
541 if ( mSaveEditsButton->isVisible() )
542 buttons |= Button::SaveChildEdits;
543 if ( mAddFeatureButton->isVisible() )
544 buttons |= Button::AddChildFeature;
545 if ( mDuplicateFeatureButton->isVisible() )
547 if ( mDeleteFeatureButton->isVisible() )
549 if ( mZoomToFeatureButton->isVisible() )
551 return buttons;
552}
553
554void QgsRelationEditorWidget::parentFormValueChanged( const QString &attribute, const QVariant &newValue )
555{
556 mDualView->parentFormValueChanged( attribute, newValue );
557}
558
559void QgsRelationEditorWidget::showContextMenu( QgsActionMenu *menu, const QgsFeatureId fid )
560{
562 {
563 QAction *qAction = nullptr;
564
565 if ( mButtonsVisibility.testFlag( QgsRelationEditorWidget::Button::DeleteChildFeature ) )
566 {
567 qAction = menu->addAction( QgsApplication::getThemeIcon( u"/mActionDeleteSelected.svg"_s ), tr( "Delete Feature" ) );
568 connect( qAction, &QAction::triggered, this, [this, fid]() { deleteFeature( fid ); } );
569 }
570
571 if ( mButtonsVisibility.testFlag( QgsRelationEditorWidget::Button::Unlink ) )
572 {
573 qAction = menu->addAction( QgsApplication::getThemeIcon( u"/mActionUnlink.svg"_s ), tr( "Unlink Feature" ) );
574 connect( qAction, &QAction::triggered, this, [this, fid]() { unlinkFeature( fid ); } );
575 }
576 }
577}
578
579void QgsRelationEditorWidget::setMapTool( QgsMapTool *mapTool )
580{
581 QgsMapCanvas *mapCanvas = mEditorContext.mapCanvas();
582
583 mapCanvas->setMapTool( mapTool );
584 mapCanvas->window()->raise();
585 mapCanvas->activateWindow();
586 mapCanvas->setFocus();
587 connect( mapTool, &QgsMapTool::deactivated, this, &QgsRelationEditorWidget::mapToolDeactivated );
588}
589
590void QgsRelationEditorWidget::unsetMapTool()
591{
592 QgsMapCanvas *mapCanvas = mEditorContext.mapCanvas();
593
594 // this will call mapToolDeactivated
595 mapCanvas->unsetMapTool( mMapToolDigitize );
596
597 disconnect( mMapToolDigitize, &QgsMapToolDigitizeFeature::digitizingCompleted, this, &QgsRelationEditorWidget::onDigitizingCompleted );
598}
599
600QgsFeatureIds QgsRelationEditorWidget::selectedChildFeatureIds() const
601{
602 if ( multiEditModeActive() )
603 {
604 QgsFeatureIds featureIds;
605 for ( QTreeWidgetItem *treeWidgetItem : mMultiEditTreeWidget->selectedItems() )
606 {
607 if ( static_cast<MultiEditFeatureType>( treeWidgetItem->data( 0, static_cast<int>( MultiEditTreeWidgetRole::FeatureType ) ).toInt() ) != MultiEditFeatureType::Child )
608 continue;
609
610 featureIds.insert( treeWidgetItem->data( 0, static_cast<int>( MultiEditTreeWidgetRole::FeatureId ) ).toLongLong() );
611 }
612 return featureIds;
613 }
614 else if ( mFeatureSelectionMgr )
615 {
616 return mFeatureSelectionMgr->selectedFeatureIds();
617 }
618 else
619 {
620 return {};
621 }
622}
623
624void QgsRelationEditorWidget::updateUiSingleEdit()
625{
626 mFormViewButton->setVisible( true );
627 mTableViewButton->setVisible( true );
628 mMultiEditInfoLabel->setVisible( false );
629
630 mStackedWidget->setCurrentWidget( mDualView );
631
632 QgsFeatureRequest request = mRelation.getRelatedFeaturesRequest( mFeatureList.first() );
633 QgsVectorLayer *layer = nullptr;
634 if ( mNmRelation.isValid() )
635 {
636 QgsFeature fet;
637 QgsFeatureRequest nmRequest;
638
639 QgsFeatureIterator it = mRelation.referencingLayer()->getFeatures( request );
640 QStringList filters;
641
642 while ( it.nextFeature( fet ) )
643 {
644 QString filter = mNmRelation.getReferencedFeatureRequest( fet ).filterExpression()->expression();
645 filters << filter.prepend( '(' ).append( ')' );
646 }
647
648 QString reducedExpression;
649 if ( QgsExpression::attemptReduceToInClause( filters, reducedExpression ) )
650 {
651 nmRequest.setFilterExpression( reducedExpression );
652 }
653 else
654 {
655 nmRequest.setFilterExpression( filters.join( " OR "_L1 ) );
656 }
657
658 request = std::move( nmRequest );
659 layer = mNmRelation.referencedLayer();
660 }
661 else if ( mRelation.referencingLayer() )
662 {
663 layer = mRelation.referencingLayer();
664 }
665
666 if ( !layer )
667 return;
668
669 // don't recreate all the widget from scratch if only the request has changed
670 if ( !mDualView->masterModel() || layer != mDualView->masterModel()->layer() )
671 {
672 initDualView( layer, request );
673 }
674 else
675 {
676 mFeatureSelectionMgr = new QgsFilteredSelectionManager( layer, request, mDualView );
677 mDualView->setFeatureSelectionManager( mFeatureSelectionMgr );
678 connect( mFeatureSelectionMgr, &QgsIFeatureSelectionManager::selectionChanged, this, &QgsRelationEditorWidget::updateButtons );
679
680 mDualView->setRequest( request );
681 mDualView->masterModel()->loadLayer();
682
683 updateButtons();
684 }
685}
686
687void QgsRelationEditorWidget::updateUiMultiEdit()
688{
689 mFormViewButton->setVisible( false );
690 mTableViewButton->setVisible( false );
691 mMultiEditInfoLabel->setVisible( true );
692
693 mStackedWidget->setCurrentWidget( mMultiEditStackedWidgetPage );
694
695 QList<QTreeWidgetItem *> parentTreeWidgetItems;
696
697 QgsFeatureIds featureIdsMixedValues;
698 QMultiMap<QTreeWidgetItem *, QgsFeatureId> multimapChildFeatures;
699
700 mMultiEditTreeWidget->clear();
701 for ( const QgsFeature &featureParent : std::as_const( mFeatureList ) )
702 {
703 QTreeWidgetItem *treeWidgetItem = createMultiEditTreeWidgetItem( featureParent, mRelation.referencedLayer(), MultiEditFeatureType::Parent );
704
705 // Parent feature items are not selectable
706 treeWidgetItem->setFlags( Qt::ItemIsEnabled );
707
708 parentTreeWidgetItems.append( treeWidgetItem );
709
710 // Get child features
711 const QgsFeatureRequest request = relation().getRelatedFeaturesRequest( featureParent );
712 QgsFeatureIterator featureIterator = mRelation.referencingLayer()->getFeatures( request );
713 QgsFeature featureChild;
714 while ( featureIterator.nextFeature( featureChild ) )
715 {
716 if ( mNmRelation.isValid() )
717 {
718 const QgsFeatureRequest requestFinalChild = mNmRelation.getReferencedFeatureRequest( featureChild );
719 QgsFeatureIterator featureIteratorFinalChild = mNmRelation.referencedLayer()->getFeatures( requestFinalChild );
720 QgsFeature featureChildChild;
721 while ( featureIteratorFinalChild.nextFeature( featureChildChild ) )
722 {
723 QTreeWidgetItem *treeWidgetItemChild = createMultiEditTreeWidgetItem( featureChildChild, mNmRelation.referencedLayer(), MultiEditFeatureType::Child );
724
725 treeWidgetItem->addChild( treeWidgetItemChild );
726
727 featureIdsMixedValues.insert( featureChildChild.id() );
728 multimapChildFeatures.insert( treeWidgetItem, featureChildChild.id() );
729 }
730 }
731 else
732 {
733 QTreeWidgetItem *treeWidgetItemChild = createMultiEditTreeWidgetItem( featureChild, mRelation.referencingLayer(), MultiEditFeatureType::Child );
734 treeWidgetItem->addChild( treeWidgetItemChild );
735
736 featureIdsMixedValues.insert( featureChild.id() );
737 }
738 }
739
740 mMultiEditTreeWidget->addTopLevelItem( treeWidgetItem );
741 treeWidgetItem->setExpanded( true );
742 }
743
744 // Set mixed values indicator (Green or Orange)
745 //
746 // Green:
747 // n:m and 1:n: 0 child features available
748 // n:m with no mixed values
749 // Orange:
750 // n:m with mixed values
751 // 1:n always, including when we pseudo know that feature are related (just added feature)
752 //
753 // See https://github.com/qgis/QGIS/pull/45703
754 //
755 if ( mNmRelation.isValid() )
756 {
757 QgsFeatureIds::iterator iterator = featureIdsMixedValues.begin();
758 while ( iterator != featureIdsMixedValues.end() )
759 {
760 bool mixedValues = false;
761 for ( QTreeWidgetItem *parentTreeWidgetItem : parentTreeWidgetItems )
762 {
763 if ( !multimapChildFeatures.values( parentTreeWidgetItem ).contains( *iterator ) )
764 {
765 mixedValues = true;
766 break;
767 }
768 }
769
770 if ( !mixedValues )
771 {
772 iterator = featureIdsMixedValues.erase( iterator );
773 continue;
774 }
775
776 ++iterator;
777 }
778 }
779
780 // Set multiedit info label
781 if ( featureIdsMixedValues.isEmpty() )
782 {
783 QIcon icon = QgsApplication::getThemeIcon( u"/multieditSameValues.svg"_s );
784 mMultiEditInfoLabel->setPixmap( icon.pixmap( mMultiEditInfoLabel->height(), mMultiEditInfoLabel->height() ) );
785 mMultiEditInfoLabel->setToolTip( tr( "All features in selection have equal relations" ) );
786 }
787 else
788 {
789 QIcon icon = QgsApplication::getThemeIcon( u"/multieditMixedValues.svg"_s );
790 mMultiEditInfoLabel->setPixmap( icon.pixmap( mMultiEditInfoLabel->height(), mMultiEditInfoLabel->height() ) );
791 mMultiEditInfoLabel->setToolTip( tr( "Some features in selection have different relations" ) );
792
793 // Set italic font for mixed values
794 QFont fontItalic = mMultiEditTreeWidget->font();
795 fontItalic.setItalic( true );
796 for ( QTreeWidgetItem *parentTreeWidgetItem : parentTreeWidgetItems )
797 {
798 for ( int childIndex = 0; childIndex < parentTreeWidgetItem->childCount(); ++childIndex )
799 {
800 QTreeWidgetItem *childItem = parentTreeWidgetItem->child( childIndex );
801 const QgsFeatureId featureIdCurrentItem = childItem->data( 0, static_cast<int>( MultiEditTreeWidgetRole::FeatureId ) ).toInt();
802 if ( featureIdsMixedValues.contains( featureIdCurrentItem ) )
803 childItem->setFont( 0, fontItalic );
804 }
805 }
806 }
807}
808
809QTreeWidgetItem *QgsRelationEditorWidget::createMultiEditTreeWidgetItem( const QgsFeature &feature, QgsVectorLayer *layer, MultiEditFeatureType type )
810{
811 QTreeWidgetItem *treeWidgetItem = new QTreeWidgetItem();
812 treeWidgetItem->setText( 0, QgsVectorLayerUtils::getFeatureDisplayString( layer, feature ) );
813 treeWidgetItem->setIcon( 0, QgsIconUtils::iconForLayer( layer ) );
814 treeWidgetItem->setData( 0, static_cast<int>( MultiEditTreeWidgetRole::FeatureType ), static_cast<int>( type ) );
815 treeWidgetItem->setData( 0, static_cast<int>( MultiEditTreeWidgetRole::FeatureId ), feature.id() );
816 return treeWidgetItem;
817}
818
819void QgsRelationEditorWidget::onDigitizingCanceled()
820{
821 digitizingFinished();
822}
823
824void QgsRelationEditorWidget::digitizingFinished()
825{
826 window()->setVisible( true );
827 window()->raise();
828 window()->activateWindow();
829 unsetMapTool();
830}
831
832void QgsRelationEditorWidget::mapToolDeactivated()
833{
834 if ( mEditorContext.mainMessageBar() && mMessageBarItem )
835 {
836 mEditorContext.mainMessageBar()->popWidget( mMessageBarItem );
837 }
838 mMessageBarItem = nullptr;
839}
840
842{
843 return QVariantMap(
844 { { "buttons", qgsFlagValueToKeys( visibleButtons() ) },
845 { "show_first_feature", mShowFirstFeature },
846 { "allow_add_child_feature_with_no_geometry", mAllowAddChildFeatureWithNoGeometry },
847 { "filter_expression", mFilterExpression } }
848 );
849}
850
852{
853 mButtonsVisibility = qgsFlagKeysToValue( config.value( u"buttons"_s ).toString(), QgsRelationEditorWidget::Button::AllButtons );
854 mShowFirstFeature = config.value( u"show_first_feature"_s, true ).toBool();
855 mAllowAddChildFeatureWithNoGeometry = config.value( u"allow_add_child_feature_with_no_geometry"_s, false ).toBool();
856 mFilterExpression = config.value( u"filter_expression"_s ).toString();
857 updateButtons();
858}
859
861{
862 Q_UNUSED( newRelation );
863 Q_UNUSED( newFeature );
864
865 if ( !mRelation.isValid() )
866 return;
867
868 disconnect( mRelation.referencingLayer(), &QgsVectorLayer::editingStarted, this, &QgsRelationEditorWidget::updateButtons );
869 disconnect( mRelation.referencingLayer(), &QgsVectorLayer::editingStopped, this, &QgsRelationEditorWidget::updateButtons );
870}
871
873{
874 if ( !mRelation.isValid() || mFeatureList.isEmpty() )
875 {
876 updateButtons();
877 return;
878 }
879
880 connect( mRelation.referencingLayer(), &QgsVectorLayer::editingStarted, this, &QgsRelationEditorWidget::updateButtons );
881 connect( mRelation.referencingLayer(), &QgsVectorLayer::editingStopped, this, &QgsRelationEditorWidget::updateButtons );
882
883 updateButtons();
884
885 const QgsFeatureRequest myRequest = mRelation.getRelatedFeaturesRequest( mFeatureList.first() );
886 initDualView( mRelation.referencingLayer(), myRequest );
887}
888
889void QgsRelationEditorWidget::beforeSetRelations( const QgsRelation &newRelation, const QgsRelation &newNmRelation )
890{
891 Q_UNUSED( newRelation );
892 Q_UNUSED( newNmRelation );
893
894 if ( mRelation.isValid() )
895 {
896 disconnect( mRelation.referencingLayer(), &QgsVectorLayer::editingStarted, this, &QgsRelationEditorWidget::updateButtons );
897 disconnect( mRelation.referencingLayer(), &QgsVectorLayer::editingStopped, this, &QgsRelationEditorWidget::updateButtons );
898 }
899
900 if ( mNmRelation.isValid() )
901 {
902 disconnect( mNmRelation.referencedLayer(), &QgsVectorLayer::editingStarted, this, &QgsRelationEditorWidget::updateButtons );
903 disconnect( mNmRelation.referencedLayer(), &QgsVectorLayer::editingStopped, this, &QgsRelationEditorWidget::updateButtons );
904 }
905}
906
908{
909 if ( !mRelation.isValid() )
910 return;
911
912 connect( mRelation.referencingLayer(), &QgsVectorLayer::editingStarted, this, &QgsRelationEditorWidget::updateButtons );
913 connect( mRelation.referencingLayer(), &QgsVectorLayer::editingStopped, this, &QgsRelationEditorWidget::updateButtons );
914
915 if ( mNmRelation.isValid() )
916 {
917 connect( mNmRelation.referencedLayer(), &QgsVectorLayer::editingStarted, this, &QgsRelationEditorWidget::updateButtons );
918 connect( mNmRelation.referencedLayer(), &QgsVectorLayer::editingStopped, this, &QgsRelationEditorWidget::updateButtons );
919 }
920
921 updateButtons();
922}
923
928
930{
931 const QgsFeatureIds selectedFids = selectedChildFeatureIds();
932 unlinkFeatures( selectedFids );
933}
934
936{
937 duplicateFeatures( mFeatureSelectionMgr->selectedFeatureIds() );
938}
939
941{
942 duplicateFeatures( mFeatureSelectionMgr->selectedFeatureIds() );
943}
944
946{
947 const QgsFeatureIds selectedFids = selectedChildFeatureIds();
948 deleteFeatures( selectedFids );
949}
950
952{
953 QgsMapCanvas *c = mEditorContext.mapCanvas();
954 if ( !c )
955 return;
956
957 c->zoomToFeatureIds( mNmRelation.isValid() ? mNmRelation.referencedLayer() : mRelation.referencingLayer(), mFeatureSelectionMgr->selectedFeatureIds() );
958}
959
961
962
965{
966 setupUi( this );
967 connect( mEditExpression, &QAbstractButton::clicked, this, &QgsRelationEditorConfigWidget::mEditExpression_clicked );
968
969 // Make filter depending on link button
970 filterExpressionLabel->setEnabled( mRelationShowLinkCheckBox->isChecked() );
971 mEditExpression->setEnabled( mRelationShowLinkCheckBox->isChecked() );
972 mFilterExpression->setEnabled( mRelationShowLinkCheckBox->isChecked() );
973 connect( mRelationShowLinkCheckBox, &QCheckBox::toggled, filterExpressionLabel, &QLabel::setEnabled );
974 connect( mRelationShowLinkCheckBox, &QCheckBox::toggled, mEditExpression, &QToolButton::setEnabled );
975 connect( mRelationShowLinkCheckBox, &QCheckBox::toggled, mFilterExpression, &QTextEdit::setEnabled );
976
977 // Make add feature with no geometry depending on add button
978 mAllowAddChildFeatureWithNoGeometry->setEnabled( mRelationShowAddChildCheckBox->isChecked() );
979 connect( mRelationShowAddChildCheckBox, &QCheckBox::toggled, mAllowAddChildFeatureWithNoGeometry, &QCheckBox::setEnabled );
980}
981
983{
984 QgsVectorLayer *vl = nullptr;
985
986 if ( nmRelation().isValid() )
987 {
989 }
990 else
991 {
992 vl = relation().referencingLayer();
993 }
994
995 // Show expression builder
997 QgsExpressionBuilderDialog dlg( vl, mFilterExpression->toPlainText(), this, u"generic"_s, context );
998 dlg.setWindowTitle( tr( "Edit Filter Expression of Target Layer" ) );
999
1000 if ( dlg.exec() == QDialog::Accepted )
1001 {
1002 mFilterExpression->setPlainText( dlg.expressionBuilder()->expressionText() );
1003 }
1004}
1005
1007{
1009 buttons.setFlag( QgsRelationEditorWidget::Button::Link, mRelationShowLinkCheckBox->isChecked() );
1010 buttons.setFlag( QgsRelationEditorWidget::Button::Unlink, mRelationShowUnlinkCheckBox->isChecked() );
1011 buttons.setFlag( QgsRelationEditorWidget::Button::AddChildFeature, mRelationShowAddChildCheckBox->isChecked() );
1012 buttons.setFlag( QgsRelationEditorWidget::Button::DuplicateChildFeature, mRelationShowDuplicateChildFeatureCheckBox->isChecked() );
1013 buttons.setFlag( QgsRelationEditorWidget::Button::ZoomToChildFeature, mRelationShowZoomToFeatureCheckBox->isChecked() );
1014 buttons.setFlag( QgsRelationEditorWidget::Button::DeleteChildFeature, mRelationDeleteChildFeatureCheckBox->isChecked() );
1015 buttons.setFlag( QgsRelationEditorWidget::Button::SaveChildEdits, mRelationShowSaveChildEditsCheckBox->isChecked() );
1016
1017 return QVariantMap(
1018 { { "buttons", qgsFlagValueToKeys( buttons ) },
1019 { "show_first_feature", mShowFirstFeature->isChecked() },
1020 { "allow_add_child_feature_with_no_geometry", mAllowAddChildFeatureWithNoGeometry->isChecked() },
1021 { "filter_expression", mFilterExpression->toPlainText() } }
1022 );
1023}
1024
1026{
1028
1029 mRelationShowLinkCheckBox->setChecked( buttons.testFlag( QgsRelationEditorWidget::Button::Link ) );
1030 mRelationShowUnlinkCheckBox->setChecked( buttons.testFlag( QgsRelationEditorWidget::Button::Unlink ) );
1031 mRelationShowAddChildCheckBox->setChecked( buttons.testFlag( QgsRelationEditorWidget::Button::AddChildFeature ) );
1032 mRelationShowDuplicateChildFeatureCheckBox->setChecked( buttons.testFlag( QgsRelationEditorWidget::Button::DuplicateChildFeature ) );
1033 mRelationShowZoomToFeatureCheckBox->setChecked( buttons.testFlag( QgsRelationEditorWidget::Button::ZoomToChildFeature ) );
1034 mRelationDeleteChildFeatureCheckBox->setChecked( buttons.testFlag( QgsRelationEditorWidget::Button::DeleteChildFeature ) );
1035 mRelationShowSaveChildEditsCheckBox->setChecked( buttons.testFlag( QgsRelationEditorWidget::Button::SaveChildEdits ) );
1036 mShowFirstFeature->setChecked( config.value( u"show_first_feature"_s, true ).toBool() );
1037 mAllowAddChildFeatureWithNoGeometry->setChecked( config.value( u"allow_add_child_feature_with_no_geometry"_s, false ).toBool() );
1038 mFilterExpression->setPlainText( config.value( u"filter_expression"_s ).toString() );
1039}
1040
1041
1043
1044
1045#ifndef SIP_RUN
1048
1050{
1051 return u"relation_editor"_s;
1052}
1053
1055{
1056 return QObject::tr( "Relation Editor" );
1057}
1058
1059QgsAbstractRelationEditorWidget *QgsRelationEditorWidgetFactory::create( const QVariantMap &config, QWidget *parent ) const
1060{
1061 return new QgsRelationEditorWidget( config, parent );
1062}
1063
1065{
1066 return static_cast<QgsAbstractRelationEditorConfigWidget *>( new QgsRelationEditorConfigWidget( relation, parent ) );
1067}
1068#endif
@ Point
Points.
Definition qgis.h:380
@ Line
Lines.
Definition qgis.h:381
@ Polygon
Polygons.
Definition qgis.h:382
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:7215
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:7237
QSet< QgsFeatureId > QgsFeatureIds
qint64 QgsFeatureId
64 bit feature ids negative numbers are used for uncommitted/newly added features