QGIS API Documentation 3.41.0-Master (3440c17df1d)
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,
226 this, static_cast<void ( QgsRelationEditorWidget::* )( int )>( &QgsRelationEditorWidget::setViewMode ) );
227 connect( mToggleEditingButton, &QAbstractButton::clicked, this, &QgsRelationEditorWidget::toggleEditing );
228 connect( mSaveEditsButton, &QAbstractButton::clicked, this, &QgsRelationEditorWidget::saveEdits );
229 connect( mAddFeatureButton, &QAbstractButton::clicked, this, &QgsRelationEditorWidget::addFeature );
230 connect( mAddFeatureGeometryButton, &QAbstractButton::clicked, this, &QgsRelationEditorWidget::addFeatureGeometry );
231 connect( mDuplicateFeatureButton, &QAbstractButton::clicked, this, &QgsRelationEditorWidget::duplicateSelectedFeatures );
232 connect( mDeleteFeatureButton, &QAbstractButton::clicked, this, &QgsRelationEditorWidget::deleteSelectedFeatures );
233 connect( mLinkFeatureButton, &QAbstractButton::clicked, this, &QgsRelationEditorWidget::linkFeature );
234 connect( mUnlinkFeatureButton, &QAbstractButton::clicked, this, &QgsRelationEditorWidget::unlinkSelectedFeatures );
235 connect( mZoomToFeatureButton, &QAbstractButton::clicked, this, &QgsRelationEditorWidget::zoomToSelectedFeatures );
236 connect( mMultiEditTreeWidget, &QTreeWidget::itemSelectionChanged, this, &QgsRelationEditorWidget::multiEditItemSelectionChanged );
237
238 // Set initial state for add/remove etc. buttons
239 updateButtons();
240
241 setLayout( rootLayout );
242}
243
244void QgsRelationEditorWidget::initDualView( QgsVectorLayer *layer, const QgsFeatureRequest &request )
245{
246 if ( multiEditModeActive() )
247 {
248 QgsLogger::warning( tr( "Dual view should not be used in multiple edit mode" ) );
249 return;
250 }
251
253 ctx.setParentFormFeature( mFeatureList.first() );
254 mDualView->init( layer, mEditorContext.mapCanvas(), request, ctx, true, mShowFirstFeature );
255 mFeatureSelectionMgr = new QgsFilteredSelectionManager( layer, request, mDualView );
256 mDualView->setFeatureSelectionManager( mFeatureSelectionMgr );
257
258 connect( mFeatureSelectionMgr, &QgsIFeatureSelectionManager::selectionChanged, this, &QgsRelationEditorWidget::updateButtons );
259
260 QIcon icon;
261 QString text;
262 if ( layer->geometryType() == Qgis::GeometryType::Point )
263 {
264 icon = QgsApplication::getThemeIcon( QStringLiteral( "/mActionCapturePoint.svg" ) );
265 text = tr( "Add Point Child Feature" );
266 }
267 else if ( layer->geometryType() == Qgis::GeometryType::Line )
268 {
269 icon = QgsApplication::getThemeIcon( QStringLiteral( "/mActionCaptureLine.svg" ) );
270 text = tr( "Add Line Child Feature" );
271 }
272 else if ( layer->geometryType() == Qgis::GeometryType::Polygon )
273 {
274 icon = QgsApplication::getThemeIcon( QStringLiteral( "/mActionCapturePolygon.svg" ) );
275 text = tr( "Add Polygon Child Feature" );
276 }
277
278 mAddFeatureGeometryButton->setIcon( icon );
279 mAddFeatureGeometryButton->setText( text );
280 mAddFeatureGeometryButton->setToolTip( text );
281
282 updateButtons();
283}
284
286{
287 mEditorContext = context;
288
289 if ( context.mapCanvas() && context.cadDockWidget() )
290 {
291 mMapToolDigitize.reset( new QgsMapToolDigitizeFeature( context.mapCanvas(), context.cadDockWidget() ) );
292 mMapToolDigitize->setButton( mAddFeatureGeometryButton );
293 }
294
295 updateButtons();
296}
297
299{
300 mDualView->setView( mode );
301 mViewMode = mode;
302}
303
304void QgsRelationEditorWidget::updateButtons()
305{
306 bool toggleEditingButtonEnabled = false;
307 bool canAdd = false;
308 bool canAddGeometry = false;
309 bool canRemove = false;
310 bool canEdit = false;
311 bool canLink = false;
312 bool canUnlink = false;
313 bool spatial = false;
314
315 if ( mRelation.isValid() )
316 {
317 toggleEditingButtonEnabled = mRelation.referencingLayer()->supportsEditing();
319 canAddGeometry = mRelation.referencingLayer()->isEditable();
320 canRemove = mRelation.referencingLayer()->isEditable();
323 canUnlink = mRelation.referencingLayer()->isEditable();
324 spatial = mRelation.referencingLayer()->isSpatial();
325 }
326
327 if ( mNmRelation.isValid() )
328 {
329 toggleEditingButtonEnabled |= mNmRelation.referencedLayer()->supportsEditing();
331 canAddGeometry = mNmRelation.referencedLayer()->isEditable();
332 canRemove = mNmRelation.referencedLayer()->isEditable();
335 }
336
337 const bool selectionNotEmpty = mFeatureSelectionMgr ? mFeatureSelectionMgr->selectedFeatureCount() : false;
338 if ( multiEditModeActive() )
339 {
340 const bool multieditLinkedChildSelected = ! selectedChildFeatureIds().isEmpty();
341
342 canAddGeometry = false;
343
344 canRemove = canRemove && multieditLinkedChildSelected;
345
346 // In 1:n relations an element can be linked only to 1 feature
347 canLink = canLink && mNmRelation.isValid();
348 canUnlink = canUnlink && multieditLinkedChildSelected;
349 }
350 else
351 {
352 canRemove = canRemove && selectionNotEmpty;
353 canUnlink = canUnlink && selectionNotEmpty;
354 }
355
356 mToggleEditingButton->setEnabled( toggleEditingButtonEnabled );
357 mAddFeatureButton->setEnabled( canAdd );
358 mAddFeatureGeometryButton->setEnabled( canAddGeometry );
359 mDuplicateFeatureButton->setEnabled( canEdit && selectionNotEmpty );
360 mLinkFeatureButton->setEnabled( canLink );
361 mDeleteFeatureButton->setEnabled( canRemove );
362 mUnlinkFeatureButton->setEnabled( canUnlink );
363 mZoomToFeatureButton->setEnabled( selectionNotEmpty );
364 mToggleEditingButton->setChecked( canEdit );
365 mSaveEditsButton->setEnabled( canEdit || canLink || canUnlink );
366
367 mToggleEditingButton->setVisible( !mLayerInSameTransactionGroup );
368
369 mLinkFeatureButton->setVisible( mButtonsVisibility.testFlag( QgsRelationEditorWidget::Button::Link ) );
370 mUnlinkFeatureButton->setVisible( mButtonsVisibility.testFlag( QgsRelationEditorWidget::Button::Unlink ) );
371 mSaveEditsButton->setVisible( mButtonsVisibility.testFlag( QgsRelationEditorWidget::Button::SaveChildEdits ) && !mLayerInSameTransactionGroup );
372 mAddFeatureButton->setVisible( mButtonsVisibility.testFlag( QgsRelationEditorWidget::Button::AddChildFeature ) && !( spatial && !mAllowAddChildFeatureWithNoGeometry ) );
373 mAddFeatureGeometryButton->setVisible( mButtonsVisibility.testFlag( QgsRelationEditorWidget::Button::AddChildFeature ) && mEditorContext.mapCanvas() && mEditorContext.cadDockWidget() && spatial );
374 mDuplicateFeatureButton->setVisible( mButtonsVisibility.testFlag( QgsRelationEditorWidget::Button::DuplicateChildFeature ) );
375 mDeleteFeatureButton->setVisible( mButtonsVisibility.testFlag( QgsRelationEditorWidget::Button::DeleteChildFeature ) );
376 mZoomToFeatureButton->setVisible( mButtonsVisibility.testFlag( QgsRelationEditorWidget::Button::ZoomToChildFeature ) && mEditorContext.mapCanvas() && spatial );
377}
378
379void QgsRelationEditorWidget::addFeature()
380{
382
383 if ( !multiEditModeActive() )
384 return;
385
386 mMultiEditTreeWidget->blockSignals( true );
387 mMultiEdit1NJustAddedIds = addedFeatures;
388 QTreeWidgetItemIterator treeWidgetItemIterator( mMultiEditTreeWidget );
389 while ( *treeWidgetItemIterator )
390 {
391 if ( ( *treeWidgetItemIterator )->data( 0, static_cast<int>( MultiEditTreeWidgetRole::FeatureType ) ).toInt() != static_cast<int>( MultiEditFeatureType::Child ) )
392 {
393 ++treeWidgetItemIterator;
394 continue;
395 }
396
397 if ( addedFeatures.contains( ( *treeWidgetItemIterator )->data( 0, static_cast<int>( MultiEditTreeWidgetRole::FeatureId ) ).toInt() ) )
398 ( *treeWidgetItemIterator )->setSelected( true );
399
400 ++treeWidgetItemIterator;
401 }
402 mMultiEditTreeWidget->blockSignals( false );
403
404 updateUi();
405 updateButtons();
406}
407
408void QgsRelationEditorWidget::addFeatureGeometry()
409{
410 if ( multiEditModeActive() )
411 {
412 QgsLogger::warning( tr( "Adding a geometry feature is not supported in multiple edit mode" ) );
413 return;
414 }
415
416 QgsVectorLayer *layer = nullptr;
417 if ( mNmRelation.isValid() )
419 else
420 layer = mRelation.referencingLayer();
421
422 mMapToolDigitize->setLayer( layer );
423
424 // window is always on top, so we hide it to digitize without seeing it
425 if ( window()->objectName() != QLatin1String( "QgisApp" ) )
426 {
427 window()->setVisible( false );
428 }
429 setMapTool( mMapToolDigitize );
430
431 connect( mMapToolDigitize, &QgsMapToolDigitizeFeature::digitizingCompleted, this, &QgsRelationEditorWidget::onDigitizingCompleted );
432
433 connect( mMapToolDigitize, &QgsMapToolDigitizeFeature::digitizingCanceled, this, &QgsRelationEditorWidget::onDigitizingCanceled );
434
435 if ( auto *lMainMessageBar = mEditorContext.mainMessageBar() )
436 {
437 const QString displayString = QgsVectorLayerUtils::getFeatureDisplayString( layer, mFeatureList.first() );
438
439 const QString title = tr( "Create child feature for parent %1 \"%2\"" ).arg( mRelation.referencedLayer()->name(), displayString );
440 const QString msg = tr( "Digitize the geometry for the new feature on layer %1. Press &lt;ESC&gt; to cancel." )
441 .arg( layer->name() );
442 mMessageBarItem = QgsMessageBar::createMessage( title, msg, this );
443 lMainMessageBar->pushItem( mMessageBarItem );
444 }
445}
446
447void QgsRelationEditorWidget::onDigitizingCompleted( const QgsFeature &feature )
448{
450 digitizingFinished();
451}
452
453void QgsRelationEditorWidget::multiEditItemSelectionChanged()
454{
455 const QList<QTreeWidgetItem *> selectedItems = mMultiEditTreeWidget->selectedItems();
456
457 // Select all items pointing to the same feature
458 // but only if we are not deselecting.
459 if ( selectedItems.size() == 1
460 && mMultiEditPreviousSelectedItems.size() <= 1 )
461 {
462 if ( selectedItems.first()->data( 0, static_cast<int>( MultiEditTreeWidgetRole::FeatureType ) ).toInt() == static_cast<int>( MultiEditFeatureType::Child ) )
463 {
464 mMultiEditTreeWidget->blockSignals( true );
465
466 const QgsFeatureId featureIdSelectedItem = selectedItems.first()->data( 0, static_cast<int>( MultiEditTreeWidgetRole::FeatureId ) ).toInt();
467
468 QTreeWidgetItemIterator treeWidgetItemIterator( mMultiEditTreeWidget );
469 while ( *treeWidgetItemIterator )
470 {
471 if ( ( *treeWidgetItemIterator )->data( 0, static_cast<int>( MultiEditTreeWidgetRole::FeatureType ) ).toInt() != static_cast<int>( MultiEditFeatureType::Child ) )
472 {
473 ++treeWidgetItemIterator;
474 continue;
475 }
476
477 const QgsFeatureId featureIdCurrentItem = ( *treeWidgetItemIterator )->data( 0, static_cast<int>( MultiEditTreeWidgetRole::FeatureId ) ).toInt();
478 if ( mNmRelation.isValid() )
479 {
480 if ( featureIdSelectedItem == featureIdCurrentItem )
481 ( *treeWidgetItemIterator )->setSelected( true );
482 }
483 else
484 {
485 if ( ! mMultiEdit1NJustAddedIds.contains( featureIdSelectedItem ) )
486 break;
487
488 if ( mMultiEdit1NJustAddedIds.contains( featureIdCurrentItem ) )
489 ( *treeWidgetItemIterator )->setSelected( true );
490 }
491
492 ++treeWidgetItemIterator;
493 }
494 mMultiEditTreeWidget->blockSignals( false );
495 }
496 }
497 mMultiEditPreviousSelectedItems = selectedItems;
498 updateButtons();
499}
500
501void QgsRelationEditorWidget::linkFeature()
502{
504}
505
506void QgsRelationEditorWidget::toggleEditing( bool state ) // cppcheck-suppress duplInheritedMember
507{
509
510 updateButtons();
511}
512
514{
515 if ( !mRelation.isValid() || mFeatureList.isEmpty() || !mFeatureList.first().isValid() )
516 return;
517
518 if ( !isVisible() )
519 return;
520
521 if ( multiEditModeActive() )
522 updateUiMultiEdit();
523 else
524 updateUiSingleEdit();
525}
526
528{
529 mButtonsVisibility = buttons;
530 updateButtons();
531}
532
534{
535 Buttons buttons;
536 if ( mLinkFeatureButton->isVisible() )
537 buttons |= Button::Link;
538 if ( mUnlinkFeatureButton->isVisible() )
539 buttons |= Button::Unlink;
540 if ( mSaveEditsButton->isVisible() )
541 buttons |= Button::SaveChildEdits;
542 if ( mAddFeatureButton->isVisible() )
543 buttons |= Button::AddChildFeature;
544 if ( mDuplicateFeatureButton->isVisible() )
546 if ( mDeleteFeatureButton->isVisible() )
548 if ( mZoomToFeatureButton->isVisible() )
550 return buttons;
551}
552
553void QgsRelationEditorWidget::parentFormValueChanged( const QString &attribute, const QVariant &newValue )
554{
555 mDualView->parentFormValueChanged( attribute, newValue );
556}
557
558void QgsRelationEditorWidget::showContextMenu( QgsActionMenu *menu, const QgsFeatureId fid )
559{
561 {
562 QAction *qAction = nullptr;
563
564 if ( mButtonsVisibility.testFlag( QgsRelationEditorWidget::Button::DeleteChildFeature ) )
565 {
566 qAction = menu->addAction( QgsApplication::getThemeIcon( QStringLiteral( "/mActionDeleteSelected.svg" ) ), tr( "Delete Feature" ) );
567 connect( qAction, &QAction::triggered, this, [this, fid]() { deleteFeature( fid ); } );
568 }
569
570 if ( mButtonsVisibility.testFlag( QgsRelationEditorWidget::Button::Unlink ) )
571 {
572 qAction = menu->addAction( QgsApplication::getThemeIcon( QStringLiteral( "/mActionUnlink.svg" ) ), tr( "Unlink Feature" ) );
573 connect( qAction, &QAction::triggered, this, [this, fid]() { unlinkFeature( fid ); } );
574 }
575 }
576}
577
578void QgsRelationEditorWidget::setMapTool( QgsMapTool *mapTool )
579{
581
582 mapCanvas->setMapTool( mapTool );
583 mapCanvas->window()->raise();
584 mapCanvas->activateWindow();
585 mapCanvas->setFocus();
586 connect( mapTool, &QgsMapTool::deactivated, this, &QgsRelationEditorWidget::mapToolDeactivated );
587}
588
589void QgsRelationEditorWidget::unsetMapTool()
590{
592
593 // this will call mapToolDeactivated
594 mapCanvas->unsetMapTool( mMapToolDigitize );
595
596 disconnect( mMapToolDigitize, &QgsMapToolDigitizeFeature::digitizingCompleted, this, &QgsRelationEditorWidget::onDigitizingCompleted );
597}
598
599QgsFeatureIds QgsRelationEditorWidget::selectedChildFeatureIds() const
600{
601 if ( multiEditModeActive() )
602 {
603 QgsFeatureIds featureIds;
604 for ( QTreeWidgetItem *treeWidgetItem : mMultiEditTreeWidget->selectedItems() )
605 {
606 if ( static_cast<MultiEditFeatureType>( treeWidgetItem->data( 0, static_cast<int>( MultiEditTreeWidgetRole::FeatureType ) ).toInt() ) != MultiEditFeatureType::Child )
607 continue;
608
609 featureIds.insert( treeWidgetItem->data( 0, static_cast<int>( MultiEditTreeWidgetRole::FeatureId ) ).toLongLong() );
610 }
611 return featureIds;
612 }
613 else
614 return mFeatureSelectionMgr->selectedFeatureIds();
615}
616
617void QgsRelationEditorWidget::updateUiSingleEdit()
618{
619 mFormViewButton->setVisible( true );
620 mTableViewButton->setVisible( true );
621 mMultiEditInfoLabel->setVisible( false );
622
623 mStackedWidget->setCurrentWidget( mDualView );
624
626 QgsVectorLayer *layer = nullptr;
627 if ( mNmRelation.isValid() )
628 {
629 QgsFeature fet;
630 QgsFeatureRequest nmRequest;
631
633 QStringList filters;
634
635 while ( it.nextFeature( fet ) )
636 {
638 filters << filter.prepend( '(' ).append( ')' );
639 }
640
641 QString reducedExpression;
642 if ( QgsExpression::attemptReduceToInClause( filters, reducedExpression ) )
643 {
644 nmRequest.setFilterExpression( reducedExpression );
645 }
646 else
647 {
648 nmRequest.setFilterExpression( filters.join( QStringLiteral( " OR " ) ) );
649 }
650
651 request = nmRequest;
653 }
654 else if ( mRelation.referencingLayer() )
655 {
656 layer = mRelation.referencingLayer();
657 }
658
659 if ( !layer )
660 return;
661
662 // don't recreate all the widget from scratch if only the request has changed
663 if ( !mDualView->masterModel() || layer != mDualView->masterModel()->layer() )
664 {
665 initDualView( layer, request );
666 }
667 else
668 {
669 mFeatureSelectionMgr = new QgsFilteredSelectionManager( layer, request, mDualView );
670 mDualView->setFeatureSelectionManager( mFeatureSelectionMgr );
671 connect( mFeatureSelectionMgr, &QgsIFeatureSelectionManager::selectionChanged, this, &QgsRelationEditorWidget::updateButtons );
672
673 mDualView->setRequest( request );
674 mDualView->masterModel()->loadLayer();
675
676 updateButtons();
677 }
678
679}
680
681void QgsRelationEditorWidget::updateUiMultiEdit()
682{
683 mFormViewButton->setVisible( false );
684 mTableViewButton->setVisible( false );
685 mMultiEditInfoLabel->setVisible( true );
686
687 mStackedWidget->setCurrentWidget( mMultiEditStackedWidgetPage ) ;
688
689 QList<QTreeWidgetItem *> parentTreeWidgetItems;
690
691 QgsFeatureIds featureIdsMixedValues;
692 QMultiMap<QTreeWidgetItem *, QgsFeatureId> multimapChildFeatures;
693
694 mMultiEditTreeWidget->clear();
695 for ( const QgsFeature &featureParent : std::as_const( mFeatureList ) )
696 {
697 QTreeWidgetItem *treeWidgetItem = createMultiEditTreeWidgetItem( featureParent, mRelation.referencedLayer(), MultiEditFeatureType::Parent );
698
699 // Parent feature items are not selectable
700 treeWidgetItem->setFlags( Qt::ItemIsEnabled );
701
702 parentTreeWidgetItems.append( treeWidgetItem );
703
704 // Get child features
705 const QgsFeatureRequest request = relation().getRelatedFeaturesRequest( featureParent );
706 QgsFeatureIterator featureIterator = mRelation.referencingLayer()->getFeatures( request );
707 QgsFeature featureChild;
708 while ( featureIterator.nextFeature( featureChild ) )
709 {
710 if ( mNmRelation.isValid() )
711 {
712 const QgsFeatureRequest requestFinalChild = mNmRelation.getReferencedFeatureRequest( featureChild );
713 QgsFeatureIterator featureIteratorFinalChild = mNmRelation.referencedLayer()->getFeatures( requestFinalChild );
714 QgsFeature featureChildChild;
715 while ( featureIteratorFinalChild.nextFeature( featureChildChild ) )
716 {
717 QTreeWidgetItem *treeWidgetItemChild = createMultiEditTreeWidgetItem( featureChildChild, mNmRelation.referencedLayer(), MultiEditFeatureType::Child );
718
719 treeWidgetItem->addChild( treeWidgetItemChild );
720
721 featureIdsMixedValues.insert( featureChildChild.id() );
722 multimapChildFeatures.insert( treeWidgetItem, featureChildChild.id() );
723 }
724 }
725 else
726 {
727 QTreeWidgetItem *treeWidgetItemChild = createMultiEditTreeWidgetItem( featureChild, mRelation.referencingLayer(), MultiEditFeatureType::Child );
728 treeWidgetItem->addChild( treeWidgetItemChild );
729
730 featureIdsMixedValues.insert( featureChild.id() );
731 }
732 }
733
734 mMultiEditTreeWidget->addTopLevelItem( treeWidgetItem );
735 treeWidgetItem->setExpanded( true );
736 }
737
738 // Set mixed values indicator (Green or Orange)
739 //
740 // Green:
741 // n:m and 1:n: 0 child features available
742 // n:m with no mixed values
743 // Orange:
744 // n:m with mixed values
745 // 1:n always, including when we pseudo know that feature are related (just added feature)
746 //
747 // See https://github.com/qgis/QGIS/pull/45703
748 //
749 if ( mNmRelation.isValid() )
750 {
751 QgsFeatureIds::iterator iterator = featureIdsMixedValues.begin();
752 while ( iterator != featureIdsMixedValues.end() )
753 {
754 bool mixedValues = false;
755 for ( QTreeWidgetItem *parentTreeWidgetItem : parentTreeWidgetItems )
756 {
757 if ( ! multimapChildFeatures.values( parentTreeWidgetItem ).contains( *iterator ) )
758 {
759 mixedValues = true;
760 break;
761 }
762 }
763
764 if ( !mixedValues )
765 {
766 iterator = featureIdsMixedValues.erase( iterator );
767 continue;
768 }
769
770 ++iterator;
771 }
772 }
773
774 // Set multiedit info label
775 if ( featureIdsMixedValues.isEmpty() )
776 {
777 QIcon icon = QgsApplication::getThemeIcon( QStringLiteral( "/multieditSameValues.svg" ) );
778 mMultiEditInfoLabel->setPixmap( icon.pixmap( mMultiEditInfoLabel->height(),
779 mMultiEditInfoLabel->height() ) );
780 mMultiEditInfoLabel->setToolTip( tr( "All features in selection have equal relations" ) );
781 }
782 else
783 {
784 QIcon icon = QgsApplication::getThemeIcon( QStringLiteral( "/multieditMixedValues.svg" ) );
785 mMultiEditInfoLabel->setPixmap( icon.pixmap( mMultiEditInfoLabel->height(),
786 mMultiEditInfoLabel->height() ) );
787 mMultiEditInfoLabel->setToolTip( tr( "Some features in selection have different relations" ) );
788
789 // Set italic font for mixed values
790 QFont fontItalic = mMultiEditTreeWidget->font();
791 fontItalic.setItalic( true );
792 for ( QTreeWidgetItem *parentTreeWidgetItem : parentTreeWidgetItems )
793 {
794 for ( int childIndex = 0; childIndex < parentTreeWidgetItem->childCount(); ++childIndex )
795 {
796 QTreeWidgetItem *childItem = parentTreeWidgetItem->child( childIndex );
797 const QgsFeatureId featureIdCurrentItem = childItem->data( 0, static_cast<int>( MultiEditTreeWidgetRole::FeatureId ) ).toInt();
798 if ( featureIdsMixedValues.contains( featureIdCurrentItem ) )
799 childItem->setFont( 0, fontItalic );
800 }
801 }
802 }
803}
804
805QTreeWidgetItem *QgsRelationEditorWidget::createMultiEditTreeWidgetItem( const QgsFeature &feature, QgsVectorLayer *layer, MultiEditFeatureType type )
806{
807 QTreeWidgetItem *treeWidgetItem = new QTreeWidgetItem();
808 treeWidgetItem->setText( 0, QgsVectorLayerUtils::getFeatureDisplayString( layer, feature ) );
809 treeWidgetItem->setIcon( 0, QgsIconUtils::iconForLayer( layer ) );
810 treeWidgetItem->setData( 0, static_cast<int>( MultiEditTreeWidgetRole::FeatureType ), static_cast<int>( type ) );
811 treeWidgetItem->setData( 0, static_cast<int>( MultiEditTreeWidgetRole::FeatureId ), feature.id() );
812 return treeWidgetItem;
813}
814
815void QgsRelationEditorWidget::onDigitizingCanceled( )
816{
817 digitizingFinished();
818}
819
820void QgsRelationEditorWidget::digitizingFinished( )
821{
822 window()->setVisible( true );
823 window()->raise();
824 window()->activateWindow();
825 unsetMapTool();
826}
827
828void QgsRelationEditorWidget::mapToolDeactivated()
829{
830 if ( mEditorContext.mainMessageBar() && mMessageBarItem )
831 {
832 mEditorContext.mainMessageBar()->popWidget( mMessageBarItem );
833 }
834 mMessageBarItem = nullptr;
835}
836
838{
839 return QVariantMap( {{"buttons", qgsFlagValueToKeys( visibleButtons() )},
840 {"show_first_feature", mShowFirstFeature},
841 {"allow_add_child_feature_with_no_geometry", mAllowAddChildFeatureWithNoGeometry },
842 {"filter_expression", mFilterExpression }
843 } );
844}
845
846void QgsRelationEditorWidget::setConfig( const QVariantMap &config )
847{
848 mButtonsVisibility = qgsFlagKeysToValue( config.value( QStringLiteral( "buttons" ) ).toString(), QgsRelationEditorWidget::Button::AllButtons );
849 mShowFirstFeature = config.value( QStringLiteral( "show_first_feature" ), true ).toBool();
850 mAllowAddChildFeatureWithNoGeometry = config.value( QStringLiteral( "allow_add_child_feature_with_no_geometry" ), false ).toBool();
851 mFilterExpression = config.value( QStringLiteral( "filter_expression" ) ).toString();
852 updateButtons();
853}
854
856{
857 Q_UNUSED( newRelation );
858 Q_UNUSED( newFeature );
859
860 if ( ! mRelation.isValid() )
861 return;
862
863 disconnect( mRelation.referencingLayer(), &QgsVectorLayer::editingStarted, this, &QgsRelationEditorWidget::updateButtons );
864 disconnect( mRelation.referencingLayer(), &QgsVectorLayer::editingStopped, this, &QgsRelationEditorWidget::updateButtons );
865}
866
868{
869 if ( ! mRelation.isValid()
870 || mFeatureList.isEmpty() )
871 {
872 updateButtons();
873 return;
874 }
875
876 connect( mRelation.referencingLayer(), &QgsVectorLayer::editingStarted, this, &QgsRelationEditorWidget::updateButtons );
877 connect( mRelation.referencingLayer(), &QgsVectorLayer::editingStopped, this, &QgsRelationEditorWidget::updateButtons );
878
879 updateButtons();
880
882 initDualView( mRelation.referencingLayer(), myRequest );
883}
884
885void QgsRelationEditorWidget::beforeSetRelations( const QgsRelation &newRelation, const QgsRelation &newNmRelation )
886{
887 Q_UNUSED( newRelation );
888 Q_UNUSED( newNmRelation );
889
890 if ( mRelation.isValid() )
891 {
892 disconnect( mRelation.referencingLayer(), &QgsVectorLayer::editingStarted, this, &QgsRelationEditorWidget::updateButtons );
893 disconnect( mRelation.referencingLayer(), &QgsVectorLayer::editingStopped, this, &QgsRelationEditorWidget::updateButtons );
894 }
895
896 if ( mNmRelation.isValid() )
897 {
898 disconnect( mNmRelation.referencedLayer(), &QgsVectorLayer::editingStarted, this, &QgsRelationEditorWidget::updateButtons );
899 disconnect( mNmRelation.referencedLayer(), &QgsVectorLayer::editingStopped, this, &QgsRelationEditorWidget::updateButtons );
900 }
901}
902
904{
905 if ( !mRelation.isValid() )
906 return;
907
908 connect( mRelation.referencingLayer(), &QgsVectorLayer::editingStarted, this, &QgsRelationEditorWidget::updateButtons );
909 connect( mRelation.referencingLayer(), &QgsVectorLayer::editingStopped, this, &QgsRelationEditorWidget::updateButtons );
910
911 if ( mNmRelation.isValid() )
912 {
913 connect( mNmRelation.referencedLayer(), &QgsVectorLayer::editingStarted, this, &QgsRelationEditorWidget::updateButtons );
914 connect( mNmRelation.referencedLayer(), &QgsVectorLayer::editingStopped, this, &QgsRelationEditorWidget::updateButtons );
915 }
916
917 updateButtons();
918}
919
924
926{
927 const QgsFeatureIds selectedFids = selectedChildFeatureIds();
928 unlinkFeatures( selectedFids );
929}
930
935
940
942{
943 const QgsFeatureIds selectedFids = selectedChildFeatureIds();
944 deleteFeatures( selectedFids );
945}
946
958
960
961
963 : QgsAbstractRelationEditorConfigWidget( relation, parent )
964{
965 setupUi( this );
966 connect( mEditExpression, &QAbstractButton::clicked, this, &QgsRelationEditorConfigWidget::mEditExpression_clicked );
967
968 // Make filter depending on link button
969 filterExpressionLabel->setEnabled( mRelationShowLinkCheckBox->isChecked() );
970 mEditExpression->setEnabled( mRelationShowLinkCheckBox->isChecked() );
971 mFilterExpression->setEnabled( mRelationShowLinkCheckBox->isChecked() );
972 connect( mRelationShowLinkCheckBox, &QCheckBox::toggled, filterExpressionLabel, &QLabel::setEnabled );
973 connect( mRelationShowLinkCheckBox, &QCheckBox::toggled, mEditExpression, &QToolButton::setEnabled );
974 connect( mRelationShowLinkCheckBox, &QCheckBox::toggled, mFilterExpression, &QTextEdit::setEnabled );
975
976 // Make add feature with no geometry depending on add button
977 mAllowAddChildFeatureWithNoGeometry->setEnabled( mRelationShowAddChildCheckBox->isChecked() );
978 connect( mRelationShowAddChildCheckBox, &QCheckBox::toggled, mAllowAddChildFeatureWithNoGeometry, &QCheckBox::setEnabled );
979}
980
982{
983 QgsVectorLayer *vl = nullptr;
984
985 if ( nmRelation().isValid() )
986 {
988 }
989 else
990 {
991 vl = relation().referencingLayer();
992 }
993
994 // Show expression builder
996 QgsExpressionBuilderDialog dlg( vl, mFilterExpression->toPlainText(), this, QStringLiteral( "generic" ), context );
997 dlg.setWindowTitle( tr( "Edit Filter Expression of Target Layer" ) );
998
999 if ( dlg.exec() == QDialog::Accepted )
1000 {
1001 mFilterExpression->setPlainText( dlg.expressionBuilder()->expressionText() );
1002 }
1003}
1004
1006{
1008 buttons.setFlag( QgsRelationEditorWidget::Button::Link, mRelationShowLinkCheckBox->isChecked() );
1009 buttons.setFlag( QgsRelationEditorWidget::Button::Unlink, mRelationShowUnlinkCheckBox->isChecked() );
1010 buttons.setFlag( QgsRelationEditorWidget::Button::AddChildFeature, mRelationShowAddChildCheckBox->isChecked() );
1011 buttons.setFlag( QgsRelationEditorWidget::Button::DuplicateChildFeature, mRelationShowDuplicateChildFeatureCheckBox->isChecked() );
1012 buttons.setFlag( QgsRelationEditorWidget::Button::ZoomToChildFeature, mRelationShowZoomToFeatureCheckBox->isChecked() );
1013 buttons.setFlag( QgsRelationEditorWidget::Button::DeleteChildFeature, mRelationDeleteChildFeatureCheckBox->isChecked() );
1014 buttons.setFlag( QgsRelationEditorWidget::Button::SaveChildEdits, mRelationShowSaveChildEditsCheckBox->isChecked() );
1015
1016 return QVariantMap(
1017 {
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
1025void QgsRelationEditorConfigWidget::setConfig( const QVariantMap &config )
1026{
1027 const QgsRelationEditorWidget::Buttons buttons = qgsFlagKeysToValue( config.value( QStringLiteral( "buttons" ) ).toString(), QgsRelationEditorWidget::Button::AllButtons );
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( QStringLiteral( "show_first_feature" ), true ).toBool() );
1037 mAllowAddChildFeatureWithNoGeometry->setChecked( config.value( QStringLiteral( "allow_add_child_feature_with_no_geometry" ), false ).toBool() );
1038 mFilterExpression->setPlainText( config.value( QStringLiteral( "filter_expression" ) ).toString() );
1039}
1040
1041
1043
1044
1045#ifndef SIP_RUN
1050
1052{
1053 return QStringLiteral( "relation_editor" );
1054}
1055
1057{
1058 return QObject::tr( "Relation Editor" );
1059}
1060
1061QgsAbstractRelationEditorWidget *QgsRelationEditorWidgetFactory::create( const QVariantMap &config, QWidget *parent ) const
1062{
1063 return new QgsRelationEditorWidget( config, parent );
1064}
1065
1067{
1068 return static_cast<QgsAbstractRelationEditorConfigWidget *>( new QgsRelationEditorConfigWidget( relation, parent ) );
1069}
1070#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:45
void showContextMenuExternally(QgsActionMenu *menu, QgsFeatureId fid)
Emitted when selecting context menu on the feature list to create the context menu individually.
ViewMode
The view modes, in which this widget can present information.
Definition qgsdualview.h:56
@ AttributeTable
Shows the features and attributes in a table layout.
Definition qgsdualview.h:61
@ AttributeEditor
Show a list of the features, where one can be chosen and the according attribute dialog will be prese...
Definition qgsdualview.h:68
void setFeatureSelectionManager(QgsIFeatureSelectionManager *featureSelectionManager)
Set the feature selection model.
void init(QgsVectorLayer *layer, QgsMapCanvas *mapCanvas, const QgsFeatureRequest &request=QgsFeatureRequest(), const QgsAttributeEditorContext &context=QgsAttributeEditorContext(), bool loadFeatures=true, bool showFirstFeature=true)
Has to be called to initialize the dual view.
void setRequest(const QgsFeatureRequest &request)
Set the request.
void parentFormValueChanged(const QString &attribute, const QVariant &value)
Called in embedded forms when an attribute value in the parent form has changed.
QgsAttributeTableModel * masterModel() const
Returns the model which has the information about all features (not only filtered)
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:6207
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:6229
QSet< QgsFeatureId > QgsFeatureIds
qint64 QgsFeatureId
64 bit feature ids negative numbers are used for uncommitted/newly added features