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