QGIS API Documentation 3.39.0-Master (d85f3c2a281)
Loading...
Searching...
No Matches
qgsrelationeditorwidget.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgsrelationeditorwidget.cpp
3 --------------------------------------
4 Date : 17.5.2013
5 Copyright : (C) 2013 Matthias Kuhn
6 Email : matthias at opengis dot ch
7 ***************************************************************************
8 * *
9 * This program is free software; you can redistribute it and/or modify *
10 * it under the terms of the GNU General Public License as published by *
11 * the Free Software Foundation; either version 2 of the License, or *
12 * (at your option) any later version. *
13 * *
14 ***************************************************************************/
15
17
18#include "qgsapplication.h"
19#include "qgsfeatureiterator.h"
20#include "qgsexpression.h"
21#include "qgsfeature.h"
22#include "qgsiconutils.h"
23#include "qgsrelation.h"
24#include "qgslogger.h"
25#include "qgsvectorlayerutils.h"
26#include "qgsmapcanvas.h"
29#include "qgsmessagebar.h"
30#include "qgsmessagebaritem.h"
31#include "qgsactionmenu.h"
34
35#include <QHBoxLayout>
36#include <QLabel>
37#include <QMessageBox>
38#include <QPushButton>
39#include <QTreeWidget>
40
43QgsFilteredSelectionManager::QgsFilteredSelectionManager( QgsVectorLayer *layer, const QgsFeatureRequest &request, QObject *parent )
44 : QgsVectorLayerSelectionManager( layer, parent )
45 , mRequest( request )
46{
47 if ( ! layer )
48 return;
49
50 for ( const auto fid : layer->selectedFeatureIds() )
51 if ( mRequest.acceptFeature( layer->getFeature( fid ) ) )
52 mSelectedFeatureIds << fid;
53
54 connect( layer, &QgsVectorLayer::selectionChanged, this, &QgsFilteredSelectionManager::onSelectionChanged );
55}
56
57const QgsFeatureIds &QgsFilteredSelectionManager::selectedFeatureIds() const
58
59
60{
61 return mSelectedFeatureIds;
62}
63
64int QgsFilteredSelectionManager::selectedFeatureCount()
65{
66 return mSelectedFeatureIds.count();
67}
68
69void QgsFilteredSelectionManager::onSelectionChanged( const QgsFeatureIds &selected, const QgsFeatureIds &deselected, bool clearAndSelect )
70{
71 QgsFeatureIds lselected = selected;
72 if ( clearAndSelect )
73 {
74 mSelectedFeatureIds.clear();
75 }
76 else
77 {
78 for ( const auto fid : deselected )
79 mSelectedFeatureIds.remove( fid );
80 }
81
82 for ( const auto fid : selected )
83 if ( mRequest.acceptFeature( layer()->getFeature( fid ) ) )
84 mSelectedFeatureIds << fid;
85 else
86 lselected.remove( fid );
87
88 emit selectionChanged( lselected, deselected, clearAndSelect );
89}
90
92
93QgsRelationEditorWidget::QgsRelationEditorWidget( const QVariantMap &config, QWidget *parent )
94 : QgsAbstractRelationEditorWidget( config, parent )
95 , mButtonsVisibility( qgsFlagKeysToValue( config.value( QStringLiteral( "buttons" ) ).toString(), QgsRelationEditorWidget::Button::AllButtons ) )
96 , mShowFirstFeature( config.value( QStringLiteral( "show_first_feature" ), true ).toBool() )
97 , mAllowAddChildFeatureWithNoGeometry( config.value( QStringLiteral( "allow_add_child_feature_with_no_geometry" ), false ).toBool() )
98 , mFilterExpression( config.value( QStringLiteral( "filter_expression" ) ).toString() )
99{
100 QVBoxLayout *rootLayout = new QVBoxLayout( this );
101 rootLayout->setContentsMargins( 0, 9, 0, 0 );
102
103 // buttons
104 QHBoxLayout *buttonLayout = new QHBoxLayout();
105 buttonLayout->setContentsMargins( 0, 0, 0, 0 );
106 // toggle editing
107 mToggleEditingButton = new QToolButton( this );
108 mToggleEditingButton->setObjectName( QStringLiteral( "mToggleEditingButton" ) );
109 mToggleEditingButton->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mActionToggleEditing.svg" ) ) );
110 mToggleEditingButton->setText( tr( "Toggle Editing" ) );
111 mToggleEditingButton->setEnabled( false );
112 mToggleEditingButton->setCheckable( true );
113 mToggleEditingButton->setToolTip( tr( "Toggle editing mode for child layer" ) );
114 buttonLayout->addWidget( mToggleEditingButton );
115 // save Edits
116 mSaveEditsButton = new QToolButton( this );
117 mSaveEditsButton->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mActionSaveEdits.svg" ) ) );
118 mSaveEditsButton->setText( tr( "Save Child Layer Edits" ) );
119 mSaveEditsButton->setToolTip( tr( "Save child layer edits" ) );
120 mSaveEditsButton->setEnabled( true );
121 buttonLayout->addWidget( mSaveEditsButton );
122 // add feature with geometry
123 mAddFeatureGeometryButton = new QToolButton( this );
124 mAddFeatureGeometryButton->setObjectName( QStringLiteral( "mAddFeatureGeometryButton" ) );
125 buttonLayout->addWidget( mAddFeatureGeometryButton );
126 // add feature
127 mAddFeatureButton = new QToolButton( this );
128 mAddFeatureButton->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mActionNewTableRow.svg" ) ) );
129 mAddFeatureButton->setText( tr( "Add Child Feature" ) );
130 mAddFeatureButton->setToolTip( tr( "Add child feature" ) );
131 mAddFeatureButton->setObjectName( QStringLiteral( "mAddFeatureButton" ) );
132 buttonLayout->addWidget( mAddFeatureButton );
133 // duplicate feature
134 mDuplicateFeatureButton = new QToolButton( this );
135 mDuplicateFeatureButton->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mActionDuplicateFeature.svg" ) ) );
136 mDuplicateFeatureButton->setText( tr( "Duplicate Child Feature(s)" ) );
137 mDuplicateFeatureButton->setToolTip( tr( "Duplicate selected child feature(s)" ) );
138 mDuplicateFeatureButton->setObjectName( QStringLiteral( "mDuplicateFeatureButton" ) );
139 buttonLayout->addWidget( mDuplicateFeatureButton );
140 // delete feature
141 mDeleteFeatureButton = new QToolButton( this );
142 mDeleteFeatureButton->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mActionDeleteSelectedFeatures.svg" ) ) );
143 mDeleteFeatureButton->setText( tr( "Delete Child Feature(s)" ) );
144 mDeleteFeatureButton->setToolTip( tr( "Delete selected child feature(s)" ) );
145 mDeleteFeatureButton->setObjectName( QStringLiteral( "mDeleteFeatureButton" ) );
146 buttonLayout->addWidget( mDeleteFeatureButton );
147 // link feature
148 mLinkFeatureButton = new QToolButton( this );
149 mLinkFeatureButton->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mActionLink.svg" ) ) );
150 mLinkFeatureButton->setText( tr( "Link Existing Feature(s)" ) );
151 mLinkFeatureButton->setToolTip( tr( "Link existing child feature(s)" ) );
152 mLinkFeatureButton->setObjectName( QStringLiteral( "mLinkFeatureButton" ) );
153 buttonLayout->addWidget( mLinkFeatureButton );
154 // unlink feature
155 mUnlinkFeatureButton = new QToolButton( this );
156 mUnlinkFeatureButton->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mActionUnlink.svg" ) ) );
157 mUnlinkFeatureButton->setText( tr( "Unlink Feature(s)" ) );
158 mUnlinkFeatureButton->setToolTip( tr( "Unlink selected child feature(s)" ) );
159 mUnlinkFeatureButton->setObjectName( QStringLiteral( "mUnlinkFeatureButton" ) );
160 buttonLayout->addWidget( mUnlinkFeatureButton );
161 // zoom to linked feature
162 mZoomToFeatureButton = new QToolButton( this );
163 mZoomToFeatureButton->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mActionZoomToSelected.svg" ) ) );
164 mZoomToFeatureButton->setText( tr( "Zoom To Feature(s)" ) );
165 mZoomToFeatureButton->setToolTip( tr( "Zoom to selected child feature(s)" ) );
166 mZoomToFeatureButton->setObjectName( QStringLiteral( "mZoomToFeatureButton" ) );
167 buttonLayout->addWidget( mZoomToFeatureButton );
168 // spacer
169 buttonLayout->addItem( new QSpacerItem( 0, 0, QSizePolicy::Expanding ) );
170 // form view
171 mFormViewButton = new QToolButton( this );
172 mFormViewButton->setText( tr( "Form View" ) );
173 mFormViewButton->setToolTip( tr( "Switch to form view" ) );
174 mFormViewButton->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mActionPropertyItem.svg" ) ) );
175 mFormViewButton->setCheckable( true );
176 mFormViewButton->setChecked( mViewMode == QgsDualView::AttributeEditor );
177 buttonLayout->addWidget( mFormViewButton );
178 // table view
179 mTableViewButton = new QToolButton( this );
180 mTableViewButton->setText( tr( "Table View" ) );
181 mTableViewButton->setToolTip( tr( "Switch to table view" ) );
182 mTableViewButton->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mActionOpenTable.svg" ) ) );
183 mTableViewButton->setCheckable( true );
184 mTableViewButton->setChecked( mViewMode == QgsDualView::AttributeTable );
185 buttonLayout->addWidget( mTableViewButton );
186 // button group
187 mViewModeButtonGroup = new QButtonGroup( this );
188 mViewModeButtonGroup->addButton( mFormViewButton, QgsDualView::AttributeEditor );
189 mViewModeButtonGroup->addButton( mTableViewButton, QgsDualView::AttributeTable );
190 // multiedit info label
191 mMultiEditInfoLabel = new QLabel( this );
192 buttonLayout->addWidget( mMultiEditInfoLabel );
193
194 // add buttons layout
195 rootLayout->addLayout( buttonLayout );
196
197 // add stacked widget
198 mStackedWidget = new QStackedWidget( this );
199
200 // add dual view (single feature content)
201 mDualView = new QgsDualView( this );
202 mDualView->setView( mViewMode );
203 connect( mDualView, &QgsDualView::showContextMenuExternally, this, &QgsRelationEditorWidget::showContextMenu );
204
205 // add multi feature editing page
206 mMultiEditStackedWidgetPage = new QWidget( this );
207 {
208 QVBoxLayout *vBoxLayout = new QVBoxLayout();
209 vBoxLayout->setContentsMargins( 0, 0, 0, 0 );
210
211 mMultiEditTreeWidget = new QTreeWidget( this );
212 mMultiEditTreeWidget->setHeaderHidden( true );
213 mMultiEditTreeWidget->setSelectionMode( QTreeWidget::ExtendedSelection );
214 vBoxLayout->addWidget( mMultiEditTreeWidget );
215
216 mMultiEditStackedWidgetPage->setLayout( vBoxLayout );
217 }
218 mStackedWidget->addWidget( mMultiEditStackedWidgetPage );
219
220 mStackedWidget->addWidget( mDualView );
221
222 rootLayout->addWidget( mStackedWidget );
223
224 connect( mViewModeButtonGroup, &QButtonGroup::idClicked,
225 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 )
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 {
629 QgsFeature fet;
630 QStringList filters;
631
632 while ( it.nextFeature( fet ) )
633 {
635 filters << filter.prepend( '(' ).append( ')' );
636 }
637
638 QgsFeatureRequest nmRequest;
639 nmRequest.setFilterExpression( filters.join( QLatin1String( " OR " ) ) );
640
641 request = nmRequest;
643 }
644 else if ( mRelation.referencingLayer() )
645 {
646 layer = mRelation.referencingLayer();
647 }
648
649 if ( !layer )
650 return;
651
652 // don't recreate all the widget from scratch if only the request has changed
653 if ( !mDualView->masterModel() || layer != mDualView->masterModel()->layer() )
654 {
655 initDualView( layer, request );
656 }
657 else
658 {
659 mFeatureSelectionMgr = new QgsFilteredSelectionManager( layer, request, mDualView );
660 mDualView->setFeatureSelectionManager( mFeatureSelectionMgr );
661 connect( mFeatureSelectionMgr, &QgsIFeatureSelectionManager::selectionChanged, this, &QgsRelationEditorWidget::updateButtons );
662
663 mDualView->setRequest( request );
664 mDualView->masterModel()->loadLayer();
665
666 updateButtons();
667 }
668}
669
670void QgsRelationEditorWidget::updateUiMultiEdit()
671{
672 mFormViewButton->setVisible( false );
673 mTableViewButton->setVisible( false );
674 mMultiEditInfoLabel->setVisible( true );
675
676 mStackedWidget->setCurrentWidget( mMultiEditStackedWidgetPage ) ;
677
678 QList<QTreeWidgetItem *> parentTreeWidgetItems;
679
680 QgsFeatureIds featureIdsMixedValues;
681 QMultiMap<QTreeWidgetItem *, QgsFeatureId> multimapChildFeatures;
682
683 mMultiEditTreeWidget->clear();
684 for ( const QgsFeature &featureParent : std::as_const( mFeatureList ) )
685 {
686 QTreeWidgetItem *treeWidgetItem = createMultiEditTreeWidgetItem( featureParent, mRelation.referencedLayer(), MultiEditFeatureType::Parent );
687
688 // Parent feature items are not selectable
689 treeWidgetItem->setFlags( Qt::ItemIsEnabled );
690
691 parentTreeWidgetItems.append( treeWidgetItem );
692
693 // Get child features
694 const QgsFeatureRequest request = relation().getRelatedFeaturesRequest( featureParent );
695 QgsFeatureIterator featureIterator = mRelation.referencingLayer()->getFeatures( request );
696 QgsFeature featureChild;
697 while ( featureIterator.nextFeature( featureChild ) )
698 {
699 if ( mNmRelation.isValid() )
700 {
701 const QgsFeatureRequest requestFinalChild = mNmRelation.getReferencedFeatureRequest( featureChild );
702 QgsFeatureIterator featureIteratorFinalChild = mNmRelation.referencedLayer()->getFeatures( requestFinalChild );
703 QgsFeature featureChildChild;
704 while ( featureIteratorFinalChild.nextFeature( featureChildChild ) )
705 {
706 QTreeWidgetItem *treeWidgetItemChild = createMultiEditTreeWidgetItem( featureChildChild, mNmRelation.referencedLayer(), MultiEditFeatureType::Child );
707
708 treeWidgetItem->addChild( treeWidgetItemChild );
709
710 featureIdsMixedValues.insert( featureChildChild.id() );
711 multimapChildFeatures.insert( treeWidgetItem, featureChildChild.id() );
712 }
713 }
714 else
715 {
716 QTreeWidgetItem *treeWidgetItemChild = createMultiEditTreeWidgetItem( featureChild, mRelation.referencingLayer(), MultiEditFeatureType::Child );
717 treeWidgetItem->addChild( treeWidgetItemChild );
718
719 featureIdsMixedValues.insert( featureChild.id() );
720 }
721 }
722
723 mMultiEditTreeWidget->addTopLevelItem( treeWidgetItem );
724 treeWidgetItem->setExpanded( true );
725 }
726
727 // Set mixed values indicator (Green or Orange)
728 //
729 // Green:
730 // n:m and 1:n: 0 child features available
731 // n:m with no mixed values
732 // Orange:
733 // n:m with mixed values
734 // 1:n always, including when we pseudo know that feature are related (just added feature)
735 //
736 // See https://github.com/qgis/QGIS/pull/45703
737 //
738 if ( mNmRelation.isValid() )
739 {
740 QgsFeatureIds::iterator iterator = featureIdsMixedValues.begin();
741 while ( iterator != featureIdsMixedValues.end() )
742 {
743 bool mixedValues = false;
744 for ( QTreeWidgetItem *parentTreeWidgetItem : parentTreeWidgetItems )
745 {
746 if ( ! multimapChildFeatures.values( parentTreeWidgetItem ).contains( *iterator ) )
747 {
748 mixedValues = true;
749 break;
750 }
751 }
752
753 if ( !mixedValues )
754 {
755 iterator = featureIdsMixedValues.erase( iterator );
756 continue;
757 }
758
759 ++iterator;
760 }
761 }
762
763 // Set multiedit info label
764 if ( featureIdsMixedValues.isEmpty() )
765 {
766 QIcon icon = QgsApplication::getThemeIcon( QStringLiteral( "/multieditSameValues.svg" ) );
767 mMultiEditInfoLabel->setPixmap( icon.pixmap( mMultiEditInfoLabel->height(),
768 mMultiEditInfoLabel->height() ) );
769 mMultiEditInfoLabel->setToolTip( tr( "All features in selection have equal relations" ) );
770 }
771 else
772 {
773 QIcon icon = QgsApplication::getThemeIcon( QStringLiteral( "/multieditMixedValues.svg" ) );
774 mMultiEditInfoLabel->setPixmap( icon.pixmap( mMultiEditInfoLabel->height(),
775 mMultiEditInfoLabel->height() ) );
776 mMultiEditInfoLabel->setToolTip( tr( "Some features in selection have different relations" ) );
777
778 // Set italic font for mixed values
779 QFont fontItalic = mMultiEditTreeWidget->font();
780 fontItalic.setItalic( true );
781 for ( QTreeWidgetItem *parentTreeWidgetItem : parentTreeWidgetItems )
782 {
783 for ( int childIndex = 0; childIndex < parentTreeWidgetItem->childCount(); ++childIndex )
784 {
785 QTreeWidgetItem *childItem = parentTreeWidgetItem->child( childIndex );
786 const QgsFeatureId featureIdCurrentItem = childItem->data( 0, static_cast<int>( MultiEditTreeWidgetRole::FeatureId ) ).toInt();
787 if ( featureIdsMixedValues.contains( featureIdCurrentItem ) )
788 childItem->setFont( 0, fontItalic );
789 }
790 }
791 }
792}
793
794QTreeWidgetItem *QgsRelationEditorWidget::createMultiEditTreeWidgetItem( const QgsFeature &feature, QgsVectorLayer *layer, MultiEditFeatureType type )
795{
796 QTreeWidgetItem *treeWidgetItem = new QTreeWidgetItem();
797 treeWidgetItem->setText( 0, QgsVectorLayerUtils::getFeatureDisplayString( layer, feature ) );
798 treeWidgetItem->setIcon( 0, QgsIconUtils::iconForLayer( layer ) );
799 treeWidgetItem->setData( 0, static_cast<int>( MultiEditTreeWidgetRole::FeatureType ), static_cast<int>( type ) );
800 treeWidgetItem->setData( 0, static_cast<int>( MultiEditTreeWidgetRole::FeatureId ), feature.id() );
801 return treeWidgetItem;
802}
803
804void QgsRelationEditorWidget::onDigitizingCanceled( )
805{
806 digitizingFinished();
807}
808
809void QgsRelationEditorWidget::digitizingFinished( )
810{
811 window()->setVisible( true );
812 window()->raise();
813 window()->activateWindow();
814 unsetMapTool();
815}
816
817void QgsRelationEditorWidget::mapToolDeactivated()
818{
819 if ( mEditorContext.mainMessageBar() && mMessageBarItem )
820 {
821 mEditorContext.mainMessageBar()->popWidget( mMessageBarItem );
822 }
823 mMessageBarItem = nullptr;
824}
825
827{
828 return QVariantMap( {{"buttons", qgsFlagValueToKeys( visibleButtons() )},
829 {"show_first_feature", mShowFirstFeature},
830 {"allow_add_child_feature_with_no_geometry", mAllowAddChildFeatureWithNoGeometry },
831 {"filter_expression", mFilterExpression }
832 } );
833}
834
835void QgsRelationEditorWidget::setConfig( const QVariantMap &config )
836{
837 mButtonsVisibility = qgsFlagKeysToValue( config.value( QStringLiteral( "buttons" ) ).toString(), QgsRelationEditorWidget::Button::AllButtons );
838 mShowFirstFeature = config.value( QStringLiteral( "show_first_feature" ), true ).toBool();
839 mAllowAddChildFeatureWithNoGeometry = config.value( QStringLiteral( "allow_add_child_feature_with_no_geometry" ), false ).toBool();
840 mFilterExpression = config.value( QStringLiteral( "filter_expression" ) ).toString();
841 updateButtons();
842}
843
845{
846 Q_UNUSED( newRelation );
847 Q_UNUSED( newFeature );
848
849 if ( ! mRelation.isValid() )
850 return;
851
852 disconnect( mRelation.referencingLayer(), &QgsVectorLayer::editingStarted, this, &QgsRelationEditorWidget::updateButtons );
853 disconnect( mRelation.referencingLayer(), &QgsVectorLayer::editingStopped, this, &QgsRelationEditorWidget::updateButtons );
854}
855
857{
858 if ( ! mRelation.isValid()
859 || mFeatureList.isEmpty() )
860 {
861 updateButtons();
862 return;
863 }
864
865 connect( mRelation.referencingLayer(), &QgsVectorLayer::editingStarted, this, &QgsRelationEditorWidget::updateButtons );
866 connect( mRelation.referencingLayer(), &QgsVectorLayer::editingStopped, this, &QgsRelationEditorWidget::updateButtons );
867
868 updateButtons();
869
871 initDualView( mRelation.referencingLayer(), myRequest );
872}
873
874void QgsRelationEditorWidget::beforeSetRelations( const QgsRelation &newRelation, const QgsRelation &newNmRelation )
875{
876 Q_UNUSED( newRelation );
877 Q_UNUSED( newNmRelation );
878
879 if ( mRelation.isValid() )
880 {
881 disconnect( mRelation.referencingLayer(), &QgsVectorLayer::editingStarted, this, &QgsRelationEditorWidget::updateButtons );
882 disconnect( mRelation.referencingLayer(), &QgsVectorLayer::editingStopped, this, &QgsRelationEditorWidget::updateButtons );
883 }
884
885 if ( mNmRelation.isValid() )
886 {
887 disconnect( mNmRelation.referencedLayer(), &QgsVectorLayer::editingStarted, this, &QgsRelationEditorWidget::updateButtons );
888 disconnect( mNmRelation.referencedLayer(), &QgsVectorLayer::editingStopped, this, &QgsRelationEditorWidget::updateButtons );
889 }
890}
891
893{
894 if ( !mRelation.isValid() )
895 return;
896
897 connect( mRelation.referencingLayer(), &QgsVectorLayer::editingStarted, this, &QgsRelationEditorWidget::updateButtons );
898 connect( mRelation.referencingLayer(), &QgsVectorLayer::editingStopped, this, &QgsRelationEditorWidget::updateButtons );
899
900 if ( mNmRelation.isValid() )
901 {
902 connect( mNmRelation.referencedLayer(), &QgsVectorLayer::editingStarted, this, &QgsRelationEditorWidget::updateButtons );
903 connect( mNmRelation.referencedLayer(), &QgsVectorLayer::editingStopped, this, &QgsRelationEditorWidget::updateButtons );
904 }
905
906 updateButtons();
907}
908
913
915{
916 const QgsFeatureIds selectedFids = selectedChildFeatureIds();
917 unlinkFeatures( selectedFids );
918}
919
924
929
931{
932 const QgsFeatureIds selectedFids = selectedChildFeatureIds();
933 deleteFeatures( selectedFids );
934}
935
947
949
950
952 : QgsAbstractRelationEditorConfigWidget( relation, parent )
953{
954 setupUi( this );
955 connect( mEditExpression, &QAbstractButton::clicked, this, &QgsRelationEditorConfigWidget::mEditExpression_clicked );
956
957 // Make filter depending on link button
958 filterExpressionLabel->setEnabled( mRelationShowLinkCheckBox->isChecked() );
959 mEditExpression->setEnabled( mRelationShowLinkCheckBox->isChecked() );
960 mFilterExpression->setEnabled( mRelationShowLinkCheckBox->isChecked() );
961 connect( mRelationShowLinkCheckBox, &QCheckBox::toggled, filterExpressionLabel, &QLabel::setEnabled );
962 connect( mRelationShowLinkCheckBox, &QCheckBox::toggled, mEditExpression, &QToolButton::setEnabled );
963 connect( mRelationShowLinkCheckBox, &QCheckBox::toggled, mFilterExpression, &QTextEdit::setEnabled );
964
965 // Make add feature with no geometry depending on add button
966 mAllowAddChildFeatureWithNoGeometry->setEnabled( mRelationShowAddChildCheckBox->isChecked() );
967 connect( mRelationShowAddChildCheckBox, &QCheckBox::toggled, mAllowAddChildFeatureWithNoGeometry, &QCheckBox::setEnabled );
968}
969
971{
972 QgsVectorLayer *vl = nullptr;
973
974 if ( nmRelation().isValid() )
975 {
977 }
978 else
979 {
980 vl = relation().referencingLayer();
981 }
982
983 // Show expression builder
985 QgsExpressionBuilderDialog dlg( vl, mFilterExpression->toPlainText(), this, QStringLiteral( "generic" ), context );
986 dlg.setWindowTitle( tr( "Edit Filter Expression of Target Layer" ) );
987
988 if ( dlg.exec() == QDialog::Accepted )
989 {
990 mFilterExpression->setPlainText( dlg.expressionBuilder()->expressionText() );
991 }
992}
993
995{
997 buttons.setFlag( QgsRelationEditorWidget::Button::Link, mRelationShowLinkCheckBox->isChecked() );
998 buttons.setFlag( QgsRelationEditorWidget::Button::Unlink, mRelationShowUnlinkCheckBox->isChecked() );
999 buttons.setFlag( QgsRelationEditorWidget::Button::AddChildFeature, mRelationShowAddChildCheckBox->isChecked() );
1000 buttons.setFlag( QgsRelationEditorWidget::Button::DuplicateChildFeature, mRelationShowDuplicateChildFeatureCheckBox->isChecked() );
1001 buttons.setFlag( QgsRelationEditorWidget::Button::ZoomToChildFeature, mRelationShowZoomToFeatureCheckBox->isChecked() );
1002 buttons.setFlag( QgsRelationEditorWidget::Button::DeleteChildFeature, mRelationDeleteChildFeatureCheckBox->isChecked() );
1003 buttons.setFlag( QgsRelationEditorWidget::Button::SaveChildEdits, mRelationShowSaveChildEditsCheckBox->isChecked() );
1004
1005 return QVariantMap(
1006 {
1007 {"buttons", qgsFlagValueToKeys( buttons )},
1008 {"show_first_feature", mShowFirstFeature->isChecked()},
1009 {"allow_add_child_feature_with_no_geometry", mAllowAddChildFeatureWithNoGeometry->isChecked()},
1010 {"filter_expression", mFilterExpression->toPlainText()}
1011 } );
1012}
1013
1014void QgsRelationEditorConfigWidget::setConfig( const QVariantMap &config )
1015{
1016 const QgsRelationEditorWidget::Buttons buttons = qgsFlagKeysToValue( config.value( QStringLiteral( "buttons" ) ).toString(), QgsRelationEditorWidget::Button::AllButtons );
1017
1018 mRelationShowLinkCheckBox->setChecked( buttons.testFlag( QgsRelationEditorWidget::Button::Link ) );
1019 mRelationShowUnlinkCheckBox->setChecked( buttons.testFlag( QgsRelationEditorWidget::Button::Unlink ) );
1020 mRelationShowAddChildCheckBox->setChecked( buttons.testFlag( QgsRelationEditorWidget::Button::AddChildFeature ) );
1021 mRelationShowDuplicateChildFeatureCheckBox->setChecked( buttons.testFlag( QgsRelationEditorWidget::Button::DuplicateChildFeature ) );
1022 mRelationShowZoomToFeatureCheckBox->setChecked( buttons.testFlag( QgsRelationEditorWidget::Button::ZoomToChildFeature ) );
1023 mRelationDeleteChildFeatureCheckBox->setChecked( buttons.testFlag( QgsRelationEditorWidget::Button::DeleteChildFeature ) );
1024 mRelationShowSaveChildEditsCheckBox->setChecked( buttons.testFlag( QgsRelationEditorWidget::Button::SaveChildEdits ) );
1025 mShowFirstFeature->setChecked( config.value( QStringLiteral( "show_first_feature" ), true ).toBool() );
1026 mAllowAddChildFeatureWithNoGeometry->setChecked( config.value( QStringLiteral( "allow_add_child_feature_with_no_geometry" ), false ).toBool() );
1027 mFilterExpression->setPlainText( config.value( QStringLiteral( "filter_expression" ) ).toString() );
1028}
1029
1030
1032
1033
1034#ifndef SIP_RUN
1039
1041{
1042 return QStringLiteral( "relation_editor" );
1043}
1044
1046{
1047 return QObject::tr( "Relation Editor" );
1048}
1049
1050QgsAbstractRelationEditorWidget *QgsRelationEditorWidgetFactory::create( const QVariantMap &config, QWidget *parent ) const
1051{
1052 return new QgsRelationEditorWidget( config, parent );
1053}
1054
1056{
1057 return static_cast<QgsAbstractRelationEditorConfigWidget *>( new QgsRelationEditorConfigWidget( relation, parent ) );
1058}
1059#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.
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:6106
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:6128
QSet< QgsFeatureId > QgsFeatureIds
qint64 QgsFeatureId
64 bit feature ids negative numbers are used for uncommitted/newly added features