QGIS API Documentation 3.39.0-Master (47f7b3a4989)
Loading...
Searching...
No Matches
qgsdualview.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgsdualview.cpp
3 --------------------------------------
4 Date : 10.2.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
16#include <QClipboard>
17#include <QDialog>
18#include <QMenu>
19#include <QMessageBox>
20#include <QProgressDialog>
21#include <QGroupBox>
22#include <QInputDialog>
23#include <QTimer>
24#include <QShortcut>
25
26#include "qgsapplication.h"
27#include "qgsactionmanager.h"
29#include "qgsdualview.h"
31#include "qgsfeaturelistmodel.h"
33#include "qgsmapcanvas.h"
34#include "qgsmaplayeraction.h"
36#include "qgsvectorlayercache.h"
39#include "qgssettings.h"
40#include "qgsscrollarea.h"
41#include "qgsgui.h"
43#include "qgsshortcutsmanager.h"
45#include "qgsmapcanvasutils.h"
46#include "qgsmessagebar.h"
48#include "qgsactionmenu.h"
49
50
51QgsDualView::QgsDualView( QWidget *parent )
52 : QStackedWidget( parent )
53{
54 setupUi( this );
55 connect( mFeatureListView, &QgsFeatureListView::aboutToChangeEditSelection, this, &QgsDualView::featureListAboutToChangeEditSelection );
56 connect( mFeatureListView, &QgsFeatureListView::currentEditSelectionChanged, this, &QgsDualView::featureListCurrentEditSelectionChanged );
57 connect( mFeatureListView, &QgsFeatureListView::currentEditSelectionProgressChanged, this, &QgsDualView::updateEditSelectionProgress );
58 connect( mFeatureListView, &QgsFeatureListView::willShowContextMenu, this, &QgsDualView::widgetWillShowContextMenu );
59
60 connect( mTableView, &QgsAttributeTableView::willShowContextMenu, this, &QgsDualView::viewWillShowContextMenu );
61 mTableView->horizontalHeader()->setContextMenuPolicy( Qt::CustomContextMenu );
62 connect( mTableView->horizontalHeader(), &QHeaderView::customContextMenuRequested, this, &QgsDualView::showViewHeaderMenu );
63 connect( mTableView, &QgsAttributeTableView::columnResized, this, &QgsDualView::tableColumnResized );
64
65 mConditionalFormatWidgetStack->hide();
66 mConditionalFormatWidget = new QgsFieldConditionalFormatWidget( this );
67 mConditionalFormatWidgetStack->setMainPanel( mConditionalFormatWidget );
68 mConditionalFormatWidget->setDockMode( true );
69
70 const QgsSettings settings;
71 mConditionalSplitter->restoreState( settings.value( QStringLiteral( "/qgis/attributeTable/splitterState" ), QByteArray() ).toByteArray() );
72
73 mPreviewColumnsMenu = new QMenu( this );
74 mActionPreviewColumnsMenu->setMenu( mPreviewColumnsMenu );
75
76 // Set preview icon
77 mActionExpressionPreview->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mIconExpressionPreview.svg" ) ) );
78
79 // Connect layer list preview signals
80 connect( mActionExpressionPreview, &QAction::triggered, this, &QgsDualView::previewExpressionBuilder );
81 connect( mFeatureListView, &QgsFeatureListView::displayExpressionChanged, this, &QgsDualView::previewExpressionChanged );
82
83 // browsing toolbar
84 connect( mNextFeatureButton, &QToolButton::clicked, mFeatureListView, &QgsFeatureListView::editNextFeature );
85 connect( mPreviousFeatureButton, &QToolButton::clicked, mFeatureListView, &QgsFeatureListView::editPreviousFeature );
86 connect( mFirstFeatureButton, &QToolButton::clicked, mFeatureListView, &QgsFeatureListView::editFirstFeature );
87 connect( mLastFeatureButton, &QToolButton::clicked, mFeatureListView, &QgsFeatureListView::editLastFeature );
88
89 auto createShortcuts = [ = ]( const QString & objectName, void ( QgsFeatureListView::* slot )() )
90 {
91 QShortcut *sc = QgsGui::shortcutsManager()->shortcutByName( objectName );
92 // do not assert for sc as it would lead to crashes in testing
93 // or when using custom widgets lib if built with Debug
94 if ( sc )
95 connect( sc, &QShortcut::activated, mFeatureListView, slot );
96 };
97 createShortcuts( QStringLiteral( "mAttributeTableFirstEditedFeature" ), &QgsFeatureListView::editFirstFeature );
98 createShortcuts( QStringLiteral( "mAttributeTablePreviousEditedFeature" ), &QgsFeatureListView::editPreviousFeature );
99 createShortcuts( QStringLiteral( "mAttributeTableNextEditedFeature" ), &QgsFeatureListView::editNextFeature );
100 createShortcuts( QStringLiteral( "mAttributeTableLastEditedFeature" ), &QgsFeatureListView::editLastFeature );
101
102 QButtonGroup *buttonGroup = new QButtonGroup( this );
103 buttonGroup->setExclusive( false );
104 buttonGroup->addButton( mAutoPanButton, PanToFeature );
105 buttonGroup->addButton( mAutoZoomButton, ZoomToFeature );
106 const FeatureListBrowsingAction action = QgsSettings().enumValue( QStringLiteral( "/qgis/attributeTable/featureListBrowsingAction" ), NoAction );
107 QAbstractButton *bt = buttonGroup->button( static_cast<int>( action ) );
108 if ( bt )
109 bt->setChecked( true );
110 connect( buttonGroup, qOverload< QAbstractButton *, bool >( &QButtonGroup::buttonToggled ), this, &QgsDualView::panZoomGroupButtonToggled );
111 mFlashButton->setChecked( QgsSettings().value( QStringLiteral( "/qgis/attributeTable/featureListHighlightFeature" ), true ).toBool() );
112 connect( mFlashButton, &QToolButton::clicked, this, &QgsDualView::flashButtonClicked );
113}
114
116{
117 QgsSettings settings;
118 settings.setValue( QStringLiteral( "/qgis/attributeTable/splitterState" ), mConditionalSplitter->saveState() );
119}
120
121void QgsDualView::init( QgsVectorLayer *layer, QgsMapCanvas *mapCanvas, const QgsFeatureRequest &request,
122 const QgsAttributeEditorContext &context, bool loadFeatures, bool showFirstFeature )
123{
124 if ( !layer )
125 return;
126
127 delete mAttributeForm;
128 mAttributeForm = nullptr;
129
130 mLayer = layer;
131
132 // Keep fields order in sync: force config reset
133 connect( mLayer, &QgsVectorLayer::updatedFields, this, [ = ]
134 {
135 mFilterModel->setAttributeTableConfig( attributeTableConfig(), /* force */ true );
136 } );
137
138 mEditorContext = context;
139
140 // create an empty form to find out if it needs geometry or not
141 const QgsAttributeForm emptyForm( mLayer, QgsFeature(), mEditorContext );
142
143 const QgsExpression sortingExpression = QgsExpression( mConfig.sortExpression() );
144
145 const bool needsGeometry = mLayer->conditionalStyles()->rulesNeedGeometry() ||
148 || emptyForm.needsGeometry()
149 || sortingExpression.needsGeometry();
150
151 initLayerCache( needsGeometry );
152 initModels( mapCanvas, request, loadFeatures );
153
154 mConditionalFormatWidget->setLayer( mLayer );
155
156 mTableView->setModel( mFilterModel );
157 mFeatureListView->setModel( mFeatureListModel );
158
159 connect( mFilterModel, &QgsAttributeTableFilterModel::sortColumnChanged, this, &QgsDualView::onSortColumnChanged );
160 connect( mLayer, &QgsVectorLayer::afterCommitChanges, this, [ = ] { mFeatureListView->setCurrentFeatureEdited( false ); } );
161
162 if ( mFeatureListPreviewButton->defaultAction() )
163 mFeatureListView->setDisplayExpression( mDisplayExpression );
164 else
165 columnBoxInit();
166
167 // This slows down load of the attribute table heaps and uses loads of memory.
168 //mTableView->resizeColumnsToContents();
169
170 if ( showFirstFeature && mFeatureListModel->rowCount( ) > 0 )
171 mFeatureListView->setEditSelection( QgsFeatureIds() << mFeatureListModel->data( mFeatureListModel->index( 0, 0 ), QgsFeatureListModel::Role::FeatureRole ).value<QgsFeature>().id() );
172}
173
174void QgsDualView::initAttributeForm( const QgsFeature &feature )
175{
176 Q_ASSERT( !mAttributeForm );
177
178 mAttributeForm = new QgsAttributeForm( mLayer, feature, mEditorContext );
179 if ( !mEditorContext.parentContext() )
180 {
181 mAttributeEditorScrollArea = new QgsScrollArea();
182 mAttributeEditorScrollArea->setWidgetResizable( true );
183 mAttributeEditor->layout()->addWidget( mAttributeEditorScrollArea );
184 mAttributeEditorScrollArea->setWidget( mAttributeForm );
185 }
186 else
187 {
188 mAttributeEditor->layout()->addWidget( mAttributeForm );
189 }
190
191 // This is an arbitrary yet small value to fix issue GH #50181
192 // the default value is 0.
193 mAttributeForm->setMinimumWidth( 200 );
194
195 setAttributeTableConfig( mLayer->attributeTableConfig() );
196
197 connect( mAttributeForm, &QgsAttributeForm::widgetValueChanged, this, &QgsDualView::featureFormAttributeChanged );
198 connect( mAttributeForm, &QgsAttributeForm::modeChanged, this, &QgsDualView::formModeChanged );
200 connect( mAttributeForm, &QgsAttributeForm::flashFeatures, this, [ = ]( const QString & filter )
201 {
202 if ( QgsMapCanvas *canvas = mFilterModel->mapCanvas() )
203 {
204 QgsMapCanvasUtils::flashMatchingFeatures( canvas, mLayer, filter );
205 }
206 } );
207 connect( mAttributeForm, &QgsAttributeForm::zoomToFeatures, this, [ = ]( const QString & filter )
208 {
209 if ( QgsMapCanvas *canvas = mFilterModel->mapCanvas() )
210 {
211 QgsMapCanvasUtils::zoomToMatchingFeatures( canvas, mLayer, filter );
212 }
213 } );
214
215 connect( mMasterModel, &QgsAttributeTableModel::modelChanged, mAttributeForm, &QgsAttributeForm::refreshFeature );
216}
217
218void QgsDualView::columnBoxInit()
219{
220 // load fields
221 const QList<QgsField> fields = mLayer->fields().toList();
222
223 const QString defaultField;
224
225 mFeatureListPreviewButton->addAction( mActionExpressionPreview );
226 mFeatureListPreviewButton->addAction( mActionPreviewColumnsMenu );
227
228 const auto constFields = fields;
229 for ( const QgsField &field : constFields )
230 {
231 const int fieldIndex = mLayer->fields().lookupField( field.name() );
232 if ( fieldIndex == -1 )
233 continue;
234
235 const QString fieldName = field.name();
236 if ( QgsGui::editorWidgetRegistry()->findBest( mLayer, fieldName ).type() != QLatin1String( "Hidden" ) )
237 {
238 const QIcon icon = mLayer->fields().iconForField( fieldIndex );
239 const QString text = mLayer->attributeDisplayName( fieldIndex );
240
241 // Generate action for the preview popup button of the feature list
242 QAction *previewAction = new QAction( icon, text, mFeatureListPreviewButton );
243 connect( previewAction, &QAction::triggered, this, [ = ] { previewColumnChanged( previewAction, fieldName ); } );
244 mPreviewColumnsMenu->addAction( previewAction );
245
246 if ( text == defaultField )
247 {
248 mFeatureListPreviewButton->setDefaultAction( previewAction );
249 }
250 }
251 }
252
253 QMenu *sortMenu = new QMenu( this );
254 QAction *sortMenuAction = new QAction( QgsApplication::getThemeIcon( QStringLiteral( "sort.svg" ) ), tr( "Sort…" ), this );
255 sortMenuAction->setMenu( sortMenu );
256
257 QAction *sortByPreviewExpressionAsc = new QAction( QgsApplication::getThemeIcon( QStringLiteral( "sort.svg" ) ), tr( "By Preview Expression (ascending)" ), this );
258 connect( sortByPreviewExpressionAsc, &QAction::triggered, this, [ = ]()
259 {
260 mFeatureListModel->setSortByDisplayExpression( true, Qt::AscendingOrder );
261 } );
262 sortMenu->addAction( sortByPreviewExpressionAsc );
263 QAction *sortByPreviewExpressionDesc = new QAction( QgsApplication::getThemeIcon( QStringLiteral( "sort-reverse.svg" ) ), tr( "By Preview Expression (descending)" ), this );
264 connect( sortByPreviewExpressionDesc, &QAction::triggered, this, [ = ]()
265 {
266 mFeatureListModel->setSortByDisplayExpression( true, Qt::DescendingOrder );
267 } );
268 sortMenu->addAction( sortByPreviewExpressionDesc );
269 QAction *sortByPreviewExpressionCustom = new QAction( QgsApplication::getThemeIcon( QStringLiteral( "mIconExpressionPreview.svg" ) ), tr( "By Custom Expression" ), this );
270 connect( sortByPreviewExpressionCustom, &QAction::triggered, this, [ = ]()
271 {
272 if ( modifySort() )
273 mFeatureListModel->setSortByDisplayExpression( false );
274 } );
275 sortMenu->addAction( sortByPreviewExpressionCustom );
276
277 mFeatureListPreviewButton->addAction( sortMenuAction );
278
279 QAction *separator = new QAction( mFeatureListPreviewButton );
280 separator->setSeparator( true );
281 mFeatureListPreviewButton->addAction( separator );
282 restoreRecentDisplayExpressions();
283
284 // If there is no single field found as preview
285 if ( !mFeatureListPreviewButton->defaultAction() )
286 {
287 mFeatureListView->setDisplayExpression( mLayer->displayExpression() );
288 mFeatureListPreviewButton->setDefaultAction( mActionExpressionPreview );
289 const QString displayExpression = mFeatureListView->displayExpression();
290 setDisplayExpression( displayExpression.isEmpty() ? tr( "'[Please define preview text]'" ) : displayExpression );
291 }
292 else
293 {
294 mFeatureListPreviewButton->defaultAction()->trigger();
295 }
296}
297
299{
300 setCurrentIndex( view );
301}
302
304{
305 return static_cast< QgsDualView::ViewMode >( currentIndex() );
306}
307
309{
310 // cleanup any existing connections
311 switch ( mFilterModel->filterMode() )
312 {
314 disconnect( mFilterModel->mapCanvas(), &QgsMapCanvas::extentsChanged, this, &QgsDualView::extentChanged );
316 break;
317
321 disconnect( mFilterModel, &QgsAttributeTableFilterModel::filterError, this, &QgsDualView::filterError );
322 break;
323
326 disconnect( mFilterModel, &QgsAttributeTableFilterModel::filterError, this, &QgsDualView::filterError );
327 disconnect( masterModel()->layer(), &QgsVectorLayer::layerModified, this, &QgsDualView::updateEditedAddedFeatures );
328 break;
329
331 disconnect( masterModel()->layer(), &QgsVectorLayer::selectionChanged, this, &QgsDualView::updateSelectedFeatures );
332 break;
333
335 mMasterModel->setShowValidityState( false );
336 break;
337 }
338
339 QgsFeatureRequest request = mMasterModel->request();
340
341 // create an empty form to find out if it needs geometry or not
342 const QgsAttributeForm emptyForm( mLayer, QgsFeature(), mEditorContext );
343 const bool needsGeometry = ( filterMode == QgsAttributeTableFilterModel::ShowVisible ) || emptyForm.needsGeometry() || QgsExpression( mConfig.sortExpression() ).needsGeometry();
344
345 const bool requiresTableReload = ( request.filterType() != Qgis::Qgis::FeatureRequestFilterType::NoFilter || request.spatialFilterType() != Qgis::SpatialFilterType::NoFilter ) // previous request was subset
346 || ( needsGeometry && request.flags() & Qgis::FeatureRequestFlag::NoGeometry ) // no geometry for last request
347 || ( mMasterModel->rowCount() == 0 ); // no features
348
349 request.setFlags( request.flags().setFlag( Qgis::FeatureRequestFlag::NoGeometry, !needsGeometry ) );
350 request.setFilterFids( QgsFeatureIds() );
351 request.setFilterRect( QgsRectangle() );
352 request.disableFilter();
353
354 // setup new connections and filter request parameters
355 switch ( filterMode )
356 {
358 connect( mFilterModel->mapCanvas(), &QgsMapCanvas::extentsChanged, this, &QgsDualView::extentChanged );
359 if ( mFilterModel->mapCanvas() )
360 {
361 const QgsRectangle rect = mFilterModel->mapCanvas()->mapSettings().mapToLayerCoordinates( mLayer, mFilterModel->mapCanvas()->extent() );
362 request.setFilterRect( rect );
363 }
365 break;
366
368 request.setFilterFids( masterModel()->layer()->editBuffer() ? masterModel()->layer()->editBuffer()->allAddedOrEditedFeatures() : QgsFeatureIds() );
370 connect( mFilterModel, &QgsAttributeTableFilterModel::filterError, this, &QgsDualView::filterError );
371 connect( masterModel()->layer(), &QgsVectorLayer::layerModified, this, &QgsDualView::updateEditedAddedFeatures );
372 break;
373
375 {
376 mMasterModel->setShowValidityState( true );
378 filterFeatures( QStringLiteral( "is_feature_valid() = false" ), context );
380 connect( mFilterModel, &QgsAttributeTableFilterModel::filterError, this, &QgsDualView::filterError );
381 break;
382 }
383
386 {
387 const QString filterExpression = filterMode == QgsAttributeTableFilterModel::ShowFilteredList ? mFilterModel->filterExpression() : QString();
388 if ( !filterExpression.isEmpty() )
389 request.setFilterExpression( mFilterModel->filterExpression() );
391 connect( mFilterModel, &QgsAttributeTableFilterModel::filterError, this, &QgsDualView::filterError );
392 break;
393 }
394
396 connect( masterModel()->layer(), &QgsVectorLayer::selectionChanged, this, &QgsDualView::updateSelectedFeatures );
397 request.setFilterFids( masterModel()->layer()->selectedFeatureIds() );
398 break;
399 }
400
401 // disable the browsing auto pan/scale if the list only shows visible items
402 switch ( filterMode )
403 {
405 setBrowsingAutoPanScaleAllowed( false );
406 break;
407
413 setBrowsingAutoPanScaleAllowed( true );
414 break;
415 }
416
417 if ( requiresTableReload )
418 {
419 //disconnect the connections of the current (old) filtermode before reload
420 mFilterModel->disconnectFilterModeConnections();
421
422 mMasterModel->setRequest( request );
423 whileBlocking( mLayerCache )->setCacheGeometry( needsGeometry );
424 mMasterModel->loadLayer();
425 }
426
427 //update filter model
428 mFilterModel->setFilterMode( filterMode );
429 emit filterChanged();
430}
431
432void QgsDualView::setSelectedOnTop( bool selectedOnTop )
433{
434 mFilterModel->setSelectedOnTop( selectedOnTop );
435}
436
437void QgsDualView::initLayerCache( bool cacheGeometry )
438{
439 // Initialize the cache
440 const QgsSettings settings;
441 const int cacheSize = settings.value( QStringLiteral( "qgis/attributeTableRowCache" ), "10000" ).toInt();
442 mLayerCache = new QgsVectorLayerCache( mLayer, cacheSize, this );
443 mLayerCache->setCacheSubsetOfAttributes( requiredAttributes( mLayer ) );
444 mLayerCache->setCacheGeometry( cacheGeometry );
445 if ( 0 == cacheSize || 0 == ( QgsVectorDataProvider::SelectAtId & mLayer->dataProvider()->capabilities() ) )
446 {
447 connect( mLayerCache, &QgsVectorLayerCache::invalidated, this, &QgsDualView::rebuildFullLayerCache );
448 rebuildFullLayerCache();
449 }
450}
451
452void QgsDualView::initModels( QgsMapCanvas *mapCanvas, const QgsFeatureRequest &request, bool loadFeatures )
453{
454 delete mFeatureListModel;
455 delete mFilterModel;
456 delete mMasterModel;
457
458 mMasterModel = new QgsAttributeTableModel( mLayerCache, this );
459 mMasterModel->setRequest( request );
460 mMasterModel->setEditorContext( mEditorContext );
461 mMasterModel->setExtraColumns( 1 ); // Add one extra column which we can "abuse" as an action column
462
463 connect( mMasterModel, &QgsAttributeTableModel::progress, this, &QgsDualView::progress );
464 connect( mMasterModel, &QgsAttributeTableModel::finished, this, &QgsDualView::finished );
465
467
468 if ( loadFeatures )
469 mMasterModel->loadLayer();
470
471 mFilterModel = new QgsAttributeTableFilterModel( mapCanvas, mMasterModel, mMasterModel );
472
473 // The following connections to invalidate() are necessary to keep the filter model in sync
474 // see regression https://github.com/qgis/QGIS/issues/23890
475 connect( mMasterModel, &QgsAttributeTableModel::rowsRemoved, mFilterModel, &QgsAttributeTableFilterModel::invalidate );
476 connect( mMasterModel, &QgsAttributeTableModel::rowsInserted, mFilterModel, &QgsAttributeTableFilterModel::invalidate );
477
479
480 mFeatureListModel = new QgsFeatureListModel( mFilterModel, mFilterModel );
481}
482
483void QgsDualView::restoreRecentDisplayExpressions()
484{
485 const QVariantList previewExpressions = mLayer->customProperty( QStringLiteral( "dualview/previewExpressions" ) ).toList();
486
487 for ( const QVariant &previewExpression : previewExpressions )
488 insertRecentlyUsedDisplayExpression( previewExpression.toString() );
489}
490
491void QgsDualView::saveRecentDisplayExpressions() const
492{
493 if ( ! mLayer )
494 {
495 return;
496 }
497 const QList<QAction *> actions = mFeatureListPreviewButton->actions();
498
499 // Remove existing same action
500 int index = actions.indexOf( mLastDisplayExpressionAction );
501 if ( index != -1 )
502 {
503 QVariantList previewExpressions;
504 for ( ; index < actions.length(); ++index )
505 {
506 QAction *action = actions.at( index );
507 previewExpressions << action->property( "previewExpression" );
508 }
509
510 mLayer->setCustomProperty( QStringLiteral( "dualview/previewExpressions" ), previewExpressions );
511 }
512}
513
514void QgsDualView::setDisplayExpression( const QString &expression )
515{
516 mDisplayExpression = expression;
517 insertRecentlyUsedDisplayExpression( expression );
518}
519
520void QgsDualView::insertRecentlyUsedDisplayExpression( const QString &expression )
521{
522 const QList<QAction *> actions = mFeatureListPreviewButton->actions();
523
524 // Remove existing same action
525 const int index = actions.indexOf( mLastDisplayExpressionAction );
526 if ( index != -1 )
527 {
528 for ( int i = 0; index + i < actions.length(); ++i )
529 {
530 QAction *action = actions.at( index );
531 if ( action->text() == expression || i >= 9 )
532 {
533 if ( action == mLastDisplayExpressionAction )
534 mLastDisplayExpressionAction = nullptr;
535 mFeatureListPreviewButton->removeAction( action );
536 }
537 else
538 {
539 if ( !mLastDisplayExpressionAction )
540 mLastDisplayExpressionAction = action;
541 }
542 }
543 }
544
545 QString name = expression;
546 QIcon icon = QgsApplication::getThemeIcon( QStringLiteral( "/mIconExpressionPreview.svg" ) );
547 if ( expression.startsWith( QLatin1String( "COALESCE( \"" ) ) && expression.endsWith( QLatin1String( ", '<NULL>' )" ) ) )
548 {
549 name = expression.mid( 11, expression.length() - 24 ); // Numbers calculated from the COALESCE / <NULL> parts
550
551 const int fieldIndex = mLayer->fields().indexOf( name );
552 if ( fieldIndex != -1 )
553 {
554 name = mLayer->attributeDisplayName( fieldIndex );
555 icon = mLayer->fields().iconForField( fieldIndex );
556 }
557 else
558 {
559 name = expression;
560 }
561 }
562
563 QAction *previewAction = new QAction( icon, name, mFeatureListPreviewButton );
564 previewAction->setProperty( "previewExpression", expression );
565 connect( previewAction, &QAction::triggered, this, [expression, this]( bool )
566 {
567 setDisplayExpression( expression );
568 mFeatureListPreviewButton->setText( expression );
569 }
570 );
571
572 mFeatureListPreviewButton->insertAction( mLastDisplayExpressionAction, previewAction );
573 mLastDisplayExpressionAction = previewAction;
574}
575
576void QgsDualView::updateEditSelectionProgress( int progress, int count )
577{
578 mProgressCount->setText( QStringLiteral( "%1 / %2" ).arg( progress + 1 ).arg( count ) );
579 mPreviousFeatureButton->setEnabled( progress > 0 );
580 mNextFeatureButton->setEnabled( progress + 1 < count );
581 mFirstFeatureButton->setEnabled( progress > 0 );
582 mLastFeatureButton->setEnabled( progress + 1 < count );
583 if ( mAttributeForm )
584 {
585 mAttributeForm->setVisible( count > 0 );
586 }
587}
588
589void QgsDualView::panOrZoomToFeature( const QgsFeatureIds &featureset )
590{
591 QgsMapCanvas *canvas = mFilterModel->mapCanvas();
592 if ( canvas && view() == AttributeEditor && featureset != mLastFeatureSet )
593 {
594 if ( mBrowsingAutoPanScaleAllowed )
595 {
596 if ( mAutoPanButton->isChecked() )
597 QTimer::singleShot( 0, this, [ = ]()
598 {
599 canvas->panToFeatureIds( mLayer, featureset, false );
600 } );
601 else if ( mAutoZoomButton->isChecked() )
602 QTimer::singleShot( 0, this, [ = ]()
603 {
604 canvas->zoomToFeatureIds( mLayer, featureset );
605 } );
606 }
607 if ( mFlashButton->isChecked() )
608 QTimer::singleShot( 0, this, [ = ]()
609 {
610 canvas->flashFeatureIds( mLayer, featureset );
611 } );
612 mLastFeatureSet = featureset;
613 }
614}
615
616void QgsDualView::setBrowsingAutoPanScaleAllowed( bool allowed )
617{
618 if ( mBrowsingAutoPanScaleAllowed == allowed )
619 return;
620
621 mBrowsingAutoPanScaleAllowed = allowed;
622
623 mAutoPanButton->setEnabled( allowed );
624 mAutoZoomButton->setEnabled( allowed );
625
626 const QString disabledHint = tr( "(disabled when attribute table only shows features visible in the current map canvas extent)" );
627
628 mAutoPanButton->setToolTip( tr( "Automatically pan to the current feature" ) + ( allowed ? QString() : QString( ' ' ) + disabledHint ) );
629 mAutoZoomButton->setToolTip( tr( "Automatically zoom to the current feature" ) + ( allowed ? QString() : QString( ' ' ) + disabledHint ) );
630}
631
632void QgsDualView::panZoomGroupButtonToggled( QAbstractButton *button, bool checked )
633{
634 if ( button == mAutoPanButton && checked )
635 {
636 QgsSettings().setEnumValue( QStringLiteral( "/qgis/attributeTable/featureListBrowsingAction" ), PanToFeature );
637 mAutoZoomButton->setChecked( false );
638 }
639 else if ( button == mAutoZoomButton && checked )
640 {
641 QgsSettings().setEnumValue( QStringLiteral( "/qgis/attributeTable/featureListBrowsingAction" ), ZoomToFeature );
642 mAutoPanButton->setChecked( false );
643 }
644 else
645 {
646 QgsSettings().setEnumValue( QStringLiteral( "/qgis/attributeTable/featureListBrowsingAction" ), NoAction );
647 }
648
649 if ( checked && mLayer->isSpatial() )
650 panOrZoomToFeature( mFeatureListView->currentEditSelection() );
651}
652
653void QgsDualView::flashButtonClicked( bool clicked )
654{
655 QgsSettings().setValue( QStringLiteral( "/qgis/attributeTable/featureListHighlightFeature" ), clicked );
656 if ( !clicked )
657 return;
658
659 QgsMapCanvas *canvas = mFilterModel->mapCanvas();
660
661 if ( canvas )
662 canvas->flashFeatureIds( mLayer, mFeatureListView->currentEditSelection() );
663}
664
665void QgsDualView::filterError( const QString &errorMessage )
666{
667 if ( mEditorContext.mainMessageBar() )
668 {
669 mEditorContext.mainMessageBar()->pushWarning( tr( "An error occurred while filtering features" ), errorMessage );
670 }
671}
672
673void QgsDualView::featureListAboutToChangeEditSelection( bool &ok )
674{
675 if ( !mAttributeForm )
676 return;
677
678 if ( mLayer->isEditable() && !mAttributeForm->save() )
679 ok = false;
680}
681
682void QgsDualView::featureListCurrentEditSelectionChanged( const QgsFeature &feat )
683{
684 if ( !mAttributeForm )
685 {
686 initAttributeForm( feat );
687 }
688 else if ( !mLayer->isEditable() || mAttributeForm->save() )
689 {
690 mAttributeForm->setFeature( feat );
691 QgsFeatureIds featureset;
692 featureset << feat.id();
693 setCurrentEditSelection( featureset );
694
695 if ( mLayer->isSpatial() )
696 panOrZoomToFeature( featureset );
697
698 }
699 else
700 {
701 // Couldn't save feature
702 }
703}
704
706{
707 mFeatureListView->setCurrentFeatureEdited( false );
708 mFeatureListView->setEditSelection( fids );
709}
710
712{
713 return mAttributeForm ? mAttributeForm->save() : false;
714}
715
717{
718 mConditionalFormatWidgetStack->setVisible( !mConditionalFormatWidgetStack->isVisible() );
719}
720
722{
723 if ( !mAttributeForm )
724 return;
725
726 if ( enabled )
727 {
728 mPreviousView = view();
730 }
731 else
732 {
733 setView( mPreviousView );
734 }
735
737}
738
740{
741 if ( !mAttributeForm )
742 return;
743
744 if ( enabled )
745 {
748 mAttributeForm->setVisible( true );
749 }
750 else
751 {
753 mAttributeForm->setVisible( mFilterModel->rowCount( ) > 0 );
754 }
755
756}
757
758void QgsDualView::previewExpressionBuilder()
759{
760 // Show expression builder
762
763 QgsExpressionBuilderDialog dlg( mLayer, mFeatureListView->displayExpression(), this, QStringLiteral( "generic" ), context );
764 dlg.setWindowTitle( tr( "Expression Based Preview" ) );
765 dlg.setExpressionText( mFeatureListView->displayExpression() );
766
767 if ( dlg.exec() == QDialog::Accepted )
768 {
769 mFeatureListView->setDisplayExpression( dlg.expressionText() );
770 mFeatureListPreviewButton->setDefaultAction( mActionExpressionPreview );
771 mFeatureListPreviewButton->setPopupMode( QToolButton::MenuButtonPopup );
772 }
773
774 setDisplayExpression( mFeatureListView->displayExpression() );
775}
776
777void QgsDualView::previewColumnChanged( QAction *previewAction, const QString &expression )
778{
779 if ( !mFeatureListView->setDisplayExpression( QStringLiteral( "COALESCE( \"%1\", '<NULL>' )" ).arg( expression ) ) )
780 {
781 QMessageBox::warning( this,
782 tr( "Column Preview" ),
783 tr( "Could not set column '%1' as preview column.\nParser error:\n%2" )
784 .arg( previewAction->text(), mFeatureListView->parserErrorString() )
785 );
786 }
787 else
788 {
789 mFeatureListPreviewButton->setText( previewAction->text() );
790 mFeatureListPreviewButton->setIcon( previewAction->icon() );
791 mFeatureListPreviewButton->setPopupMode( QToolButton::InstantPopup );
792 }
793
794 setDisplayExpression( mFeatureListView->displayExpression() );
795}
796
798{
799 return mMasterModel->rowCount();
800}
801
803{
804 return mFilterModel->rowCount();
805}
806
808{
809 const QModelIndex currentIndex = mTableView->currentIndex();
810 if ( !currentIndex.isValid() )
811 {
812 return;
813 }
814
815 const QVariant var = mMasterModel->data( currentIndex, Qt::DisplayRole );
816 QApplication::clipboard()->setText( var.toString() );
817}
818
820{
821 if ( mProgressDlg )
822 mProgressDlg->cancel();
823}
824
825void QgsDualView::parentFormValueChanged( const QString &attribute, const QVariant &newValue )
826{
827 if ( mAttributeForm )
828 {
829 mAttributeForm->parentFormValueChanged( attribute, newValue );
830 }
831}
832
833void QgsDualView::hideEvent( QHideEvent *event )
834{
835 Q_UNUSED( event )
836 saveRecentDisplayExpressions();
837}
838
839void QgsDualView::viewWillShowContextMenu( QMenu *menu, const QModelIndex &masterIndex )
840{
841 if ( !menu )
842 {
843 return;
844 }
845
846 QAction *copyContentAction = menu->addAction( tr( "Copy Cell Content" ) );
847 menu->addAction( copyContentAction );
848 connect( copyContentAction, &QAction::triggered, this, [masterIndex, this]
849 {
850 const QVariant var = mMasterModel->data( masterIndex, Qt::DisplayRole );
851 QApplication::clipboard()->setText( var.toString() );
852 } );
853
854 QgsVectorLayer *vl = mFilterModel->layer();
855 QgsMapCanvas *canvas = mFilterModel->mapCanvas();
856 if ( canvas && vl && vl->isSpatial() )
857 {
858 QAction *zoomToFeatureAction = menu->addAction( tr( "Zoom to Feature" ) );
859 connect( zoomToFeatureAction, &QAction::triggered, this, &QgsDualView::zoomToCurrentFeature );
860
861 QAction *panToFeatureAction = menu->addAction( tr( "Pan to Feature" ) );
862 connect( panToFeatureAction, &QAction::triggered, this, &QgsDualView::panToCurrentFeature );
863
864 QAction *flashFeatureAction = menu->addAction( tr( "Flash Feature" ) );
865 connect( flashFeatureAction, &QAction::triggered, this, &QgsDualView::flashCurrentFeature );
866 }
867
868 //add user-defined actions to context menu
869 const QList<QgsAction> actions = mLayer->actions()->actions( QStringLiteral( "Field" ) );
870 if ( !actions.isEmpty() )
871 {
872 QAction *a = menu->addAction( tr( "Run Layer Action" ) );
873 a->setEnabled( false );
874
875 for ( const QgsAction &action : actions )
876 {
877 if ( !action.runable() )
878 continue;
879
880 if ( vl && !vl->isEditable() && action.isEnabledOnlyWhenEditable() )
881 continue;
882
883 QgsAttributeTableAction *a = new QgsAttributeTableAction( action.name(), this, action.id(), masterIndex );
884 menu->addAction( action.name(), a, &QgsAttributeTableAction::execute );
885 }
886 }
887 const QModelIndex rowSourceIndex = mMasterModel->index( masterIndex.row(), 0 );
888 if ( ! rowSourceIndex.isValid() )
889 {
890 return;
891 }
892
893 //add actions from QgsMapLayerActionRegistry to context menu
895 const QList<QgsMapLayerAction *> registeredActions = QgsGui::mapLayerActionRegistry()->mapLayerActions( mLayer, Qgis::MapLayerActionTarget::Layer | Qgis::MapLayerActionTarget::SingleFeature, context );
896 if ( !registeredActions.isEmpty() )
897 {
898 //add a separator between user defined and standard actions
899 menu->addSeparator();
900
901 for ( QgsMapLayerAction *action : registeredActions )
902 {
903 QgsAttributeTableMapLayerAction *a = new QgsAttributeTableMapLayerAction( action->text(), this, action, rowSourceIndex );
904 menu->addAction( action->text(), a, &QgsAttributeTableMapLayerAction::execute );
905 }
906 }
907
908 // entries for multiple features layer actions
909 // only show if the context menu is shown over a selected row
910 const QgsFeatureId currentFid = mMasterModel->rowToId( masterIndex.row() );
911 if ( mLayer->selectedFeatureCount() > 1 && mLayer->selectedFeatureIds().contains( currentFid ) )
912 {
913 const QList<QgsMapLayerAction *> registeredActions = QgsGui::mapLayerActionRegistry()->mapLayerActions( mLayer, Qgis::MapLayerActionTarget::MultipleFeatures, context );
914 if ( !registeredActions.isEmpty() )
915 {
916 menu->addSeparator();
917 QAction *action = menu->addAction( tr( "Actions on Selection (%1)" ).arg( mLayer->selectedFeatureCount() ) );
918 action->setEnabled( false );
919
921 for ( QgsMapLayerAction *action : registeredActions )
922 {
923 menu->addAction( action->text(), action, [ = ]()
924 {
925 Q_NOWARN_DEPRECATED_PUSH
926 action->triggerForFeatures( mLayer, mLayer->selectedFeatures() );
927 Q_NOWARN_DEPRECATED_POP
928 action->triggerForFeatures( mLayer, mLayer->selectedFeatures(), context );
929 } );
930 }
931 }
932 }
933
934 menu->addSeparator();
935 QgsAttributeTableAction *a = new QgsAttributeTableAction( tr( "Open Form" ), this, QUuid(), rowSourceIndex );
936 menu->addAction( tr( "Open Form…" ), a, &QgsAttributeTableAction::featureForm );
937}
938
939
940void QgsDualView::widgetWillShowContextMenu( QgsActionMenu *menu, const QModelIndex &atIndex )
941{
942 emit showContextMenuExternally( menu, mFilterModel->rowToId( atIndex ) );
943}
944
945
946void QgsDualView::showViewHeaderMenu( QPoint point )
947{
948 const int col = mTableView->columnAt( point.x() );
949
950 delete mHorizontalHeaderMenu;
951 mHorizontalHeaderMenu = new QMenu( this );
952
953 QAction *hide = new QAction( tr( "&Hide Column" ), mHorizontalHeaderMenu );
954 connect( hide, &QAction::triggered, this, &QgsDualView::hideColumn );
955 hide->setData( col );
956 mHorizontalHeaderMenu->addAction( hide );
957 QAction *setWidth = new QAction( tr( "&Set Width…" ), mHorizontalHeaderMenu );
958 connect( setWidth, &QAction::triggered, this, &QgsDualView::resizeColumn );
959 setWidth->setData( col );
960 mHorizontalHeaderMenu->addAction( setWidth );
961
962 QAction *setWidthAllColumns = new QAction( tr( "&Set All Column Widths…" ), mHorizontalHeaderMenu );
963 connect( setWidthAllColumns, &QAction::triggered, this, &QgsDualView::resizeAllColumns );
964 setWidthAllColumns->setData( col );
965 mHorizontalHeaderMenu->addAction( setWidthAllColumns );
966
967 QAction *optimizeWidth = new QAction( tr( "&Autosize" ), mHorizontalHeaderMenu );
968 connect( optimizeWidth, &QAction::triggered, this, &QgsDualView::autosizeColumn );
969 optimizeWidth->setData( col );
970 mHorizontalHeaderMenu->addAction( optimizeWidth );
971
972 QAction *optimizeWidthAllColumns = new QAction( tr( "&Autosize All Columns" ), mHorizontalHeaderMenu );
973 connect( optimizeWidthAllColumns, &QAction::triggered, this, &QgsDualView::autosizeAllColumns );
974 mHorizontalHeaderMenu->addAction( optimizeWidthAllColumns );
975
976
977 mHorizontalHeaderMenu->addSeparator();
978 QAction *organize = new QAction( tr( "&Organize Columns…" ), mHorizontalHeaderMenu );
979 connect( organize, &QAction::triggered, this, &QgsDualView::organizeColumns );
980 mHorizontalHeaderMenu->addAction( organize );
981 QAction *sort = new QAction( tr( "&Sort…" ), mHorizontalHeaderMenu );
982 connect( sort, &QAction::triggered, this, [ = ]() {modifySort();} );
983 mHorizontalHeaderMenu->addAction( sort );
984
985 mHorizontalHeaderMenu->popup( mTableView->horizontalHeader()->mapToGlobal( point ) );
986}
987
988void QgsDualView::organizeColumns()
989{
990 if ( !mLayer )
991 {
992 return;
993 }
994
995 QgsOrganizeTableColumnsDialog dialog( mLayer, attributeTableConfig(), this );
996 if ( dialog.exec() == QDialog::Accepted )
997 {
998 const QgsAttributeTableConfig config = dialog.config();
999 setAttributeTableConfig( config );
1000 }
1001}
1002
1003void QgsDualView::tableColumnResized( int column, int width )
1004{
1005 QgsAttributeTableConfig config = mConfig;
1006 const int sourceCol = config.mapVisibleColumnToIndex( column );
1007 if ( sourceCol >= 0 && config.columnWidth( sourceCol ) != width )
1008 {
1009 config.setColumnWidth( sourceCol, width );
1010 setAttributeTableConfig( config );
1011 }
1012}
1013
1014void QgsDualView::hideColumn()
1015{
1016 QAction *action = qobject_cast<QAction *>( sender() );
1017 const int col = action->data().toInt();
1018 QgsAttributeTableConfig config = mConfig;
1019 const int sourceCol = mConfig.mapVisibleColumnToIndex( col );
1020 if ( sourceCol >= 0 )
1021 {
1022 config.setColumnHidden( sourceCol, true );
1023 setAttributeTableConfig( config );
1024 }
1025}
1026
1027void QgsDualView::resizeColumn()
1028{
1029 QAction *action = qobject_cast<QAction *>( sender() );
1030 const int col = action->data().toInt();
1031 if ( col < 0 )
1032 return;
1033
1034 QgsAttributeTableConfig config = mConfig;
1035 const int sourceCol = config.mapVisibleColumnToIndex( col );
1036 if ( sourceCol >= 0 )
1037 {
1038 bool ok = false;
1039 const int width = QInputDialog::getInt( this, tr( "Set column width" ), tr( "Enter column width" ),
1040 mTableView->columnWidth( col ),
1041 0, 1000, 10, &ok );
1042 if ( ok )
1043 {
1044 config.setColumnWidth( sourceCol, width );
1045 setAttributeTableConfig( config );
1046 }
1047 }
1048}
1049
1050void QgsDualView::resizeAllColumns()
1051{
1052 QAction *action = qobject_cast<QAction *>( sender() );
1053 const int col = action->data().toInt();
1054 if ( col < 0 )
1055 return;
1056
1057 QgsAttributeTableConfig config = mConfig;
1058
1059 bool ok = false;
1060 const int width = QInputDialog::getInt( this, tr( "Set Column Width" ), tr( "Enter column width" ),
1061 mTableView->columnWidth( col ),
1062 1, 1000, 10, &ok );
1063 if ( ok )
1064 {
1065 const int colCount = mTableView->model()->columnCount();
1066 if ( colCount > 0 )
1067 {
1068 for ( int i = 0; i < colCount; i++ )
1069 {
1070 config.setColumnWidth( i, width );
1071 }
1072 setAttributeTableConfig( config );
1073 }
1074 }
1075}
1076
1077void QgsDualView::autosizeColumn()
1078{
1079 QAction *action = qobject_cast<QAction *>( sender() );
1080 const int col = action->data().toInt();
1081 mTableView->resizeColumnToContents( col );
1082}
1083
1084void QgsDualView::autosizeAllColumns()
1085{
1086 mTableView->resizeColumnsToContents();
1087}
1088
1089bool QgsDualView::modifySort()
1090{
1091 if ( !mLayer )
1092 return false;
1093
1094 QgsAttributeTableConfig config = mConfig;
1095
1096 QDialog orderByDlg;
1097 orderByDlg.setWindowTitle( tr( "Configure Attribute Table Sort Order" ) );
1098 QDialogButtonBox *dialogButtonBox = new QDialogButtonBox( QDialogButtonBox::Ok | QDialogButtonBox::Cancel );
1099 QGridLayout *layout = new QGridLayout();
1100 connect( dialogButtonBox, &QDialogButtonBox::accepted, &orderByDlg, &QDialog::accept );
1101 connect( dialogButtonBox, &QDialogButtonBox::rejected, &orderByDlg, &QDialog::reject );
1102 orderByDlg.setLayout( layout );
1103
1104 QGroupBox *sortingGroupBox = new QGroupBox();
1105 sortingGroupBox->setTitle( tr( "Defined sort order in attribute table" ) );
1106 sortingGroupBox->setCheckable( true );
1107 sortingGroupBox->setChecked( !sortExpression().isEmpty() );
1108 layout->addWidget( sortingGroupBox );
1109 sortingGroupBox->setLayout( new QGridLayout() );
1110
1111 QgsExpressionBuilderWidget *expressionBuilder = new QgsExpressionBuilderWidget();
1113
1114 expressionBuilder->initWithLayer( mLayer, context, QStringLiteral( "generic" ) );
1115 expressionBuilder->setExpressionText( sortExpression().isEmpty() ? mLayer->displayExpression() : sortExpression() );
1116
1117 sortingGroupBox->layout()->addWidget( expressionBuilder );
1118
1119 QCheckBox *cbxSortAscending = new QCheckBox( tr( "Sort ascending" ) );
1120 cbxSortAscending->setChecked( config.sortOrder() == Qt::AscendingOrder );
1121 sortingGroupBox->layout()->addWidget( cbxSortAscending );
1122
1123 layout->addWidget( dialogButtonBox );
1124 if ( orderByDlg.exec() )
1125 {
1126 const Qt::SortOrder sortOrder = cbxSortAscending->isChecked() ? Qt::AscendingOrder : Qt::DescendingOrder;
1127 if ( sortingGroupBox->isChecked() )
1128 {
1129 setSortExpression( expressionBuilder->expressionText(), sortOrder );
1130 config.setSortExpression( expressionBuilder->expressionText() );
1131 config.setSortOrder( sortOrder );
1132 }
1133 else
1134 {
1135 setSortExpression( QString(), sortOrder );
1136 config.setSortExpression( QString() );
1137 }
1138
1139 return true;
1140 }
1141 else
1142 {
1143 return false;
1144 }
1145
1146}
1147
1149{
1150 QSet<int> attributes;
1151
1152 const QgsAttributeTableConfig config { layer->attributeTableConfig() };
1153
1154 const QVector<QgsAttributeTableConfig::ColumnConfig> constColumnconfigs { config.columns() };
1155 for ( const QgsAttributeTableConfig::ColumnConfig &columnConfig : std::as_const( constColumnconfigs ) )
1156 {
1157 if ( columnConfig.type == QgsAttributeTableConfig::Type::Field && ! columnConfig.hidden )
1158 {
1159 attributes.insert( layer->fields().lookupField( columnConfig.name ) );
1160 }
1161 }
1162
1163 const QSet<int> colAttrs { attributes };
1164 for ( const int attrIdx : std::as_const( colAttrs ) )
1165 {
1166 if ( layer->fields().fieldOrigin( attrIdx ) == Qgis::FieldOrigin::Expression )
1167 {
1168 attributes += QgsExpression( layer->expressionField( attrIdx ) ).referencedAttributeIndexes( layer->fields() );
1169 }
1170 }
1171
1172 QgsAttributeList attrs { attributes.values() };
1173 std::sort( attrs.begin(), attrs.end() );
1174 return attrs;
1175}
1176
1177void QgsDualView::zoomToCurrentFeature()
1178{
1179 const QModelIndex currentIndex = mTableView->currentIndex();
1180 if ( !currentIndex.isValid() )
1181 {
1182 return;
1183 }
1184
1185 QgsFeatureIds ids;
1186 ids.insert( mFilterModel->rowToId( currentIndex ) );
1187 QgsMapCanvas *canvas = mFilterModel->mapCanvas();
1188 if ( canvas )
1189 {
1190 canvas->zoomToFeatureIds( mLayer, ids );
1191 }
1192}
1193
1194void QgsDualView::panToCurrentFeature()
1195{
1196 const QModelIndex currentIndex = mTableView->currentIndex();
1197 if ( !currentIndex.isValid() )
1198 {
1199 return;
1200 }
1201
1202 QgsFeatureIds ids;
1203 ids.insert( mFilterModel->rowToId( currentIndex ) );
1204 QgsMapCanvas *canvas = mFilterModel->mapCanvas();
1205 if ( canvas )
1206 {
1207 canvas->panToFeatureIds( mLayer, ids );
1208 }
1209}
1210
1211void QgsDualView::flashCurrentFeature()
1212{
1213 const QModelIndex currentIndex = mTableView->currentIndex();
1214 if ( !currentIndex.isValid() )
1215 {
1216 return;
1217 }
1218
1219 QgsFeatureIds ids;
1220 ids.insert( mFilterModel->rowToId( currentIndex ) );
1221 QgsMapCanvas *canvas = mFilterModel->mapCanvas();
1222 if ( canvas )
1223 {
1224 canvas->flashFeatureIds( mLayer, ids );
1225 }
1226}
1227
1228void QgsDualView::rebuildFullLayerCache()
1229{
1230 connect( mLayerCache, &QgsVectorLayerCache::progress, this, &QgsDualView::progress, Qt::UniqueConnection );
1231 connect( mLayerCache, &QgsVectorLayerCache::finished, this, &QgsDualView::finished, Qt::UniqueConnection );
1232
1233 mLayerCache->setFullCache( true );
1234}
1235
1236void QgsDualView::previewExpressionChanged( const QString &expression )
1237{
1238 mLayer->setDisplayExpression( expression );
1239}
1240
1241void QgsDualView::onSortColumnChanged()
1242{
1244 if ( cfg.sortExpression() != mFilterModel->sortExpression() ||
1245 cfg.sortOrder() != mFilterModel->sortOrder() )
1246 {
1247 cfg.setSortExpression( mFilterModel->sortExpression() );
1248 cfg.setSortOrder( mFilterModel->sortOrder() );
1250 }
1251}
1252
1253void QgsDualView::updateSelectedFeatures()
1254{
1255 QgsFeatureRequest r = mMasterModel->request();
1257 return; // already requested all features
1258
1259 r.setFilterFids( masterModel()->layer()->selectedFeatureIds() );
1260 mMasterModel->setRequest( r );
1261 mMasterModel->loadLayer();
1262 emit filterChanged();
1263}
1264
1265void QgsDualView::updateEditedAddedFeatures()
1266{
1267 QgsFeatureRequest r = mMasterModel->request();
1269 return; // already requested all features
1270
1271 r.setFilterFids( masterModel()->layer()->editBuffer() ? masterModel()->layer()->editBuffer()->allAddedOrEditedFeatures() : QgsFeatureIds() );
1272 mMasterModel->setRequest( r );
1273 mMasterModel->loadLayer();
1274 emit filterChanged();
1275}
1276
1277void QgsDualView::extentChanged()
1278{
1279 QgsFeatureRequest r = mMasterModel->request();
1280 if ( mFilterModel->mapCanvas() && ( r.filterType() != Qgis::FeatureRequestFilterType::NoFilter || !r.filterRect().isNull() ) )
1281 {
1282 const QgsRectangle rect = mFilterModel->mapCanvas()->mapSettings().mapToLayerCoordinates( mLayer, mFilterModel->mapCanvas()->extent() );
1283 r.setFilterRect( rect );
1284 mMasterModel->setRequest( r );
1285 mMasterModel->loadLayer();
1286 }
1287 emit filterChanged();
1288}
1289
1290void QgsDualView::featureFormAttributeChanged( const QString &attribute, const QVariant &value, bool attributeChanged )
1291{
1292 Q_UNUSED( attribute )
1293 Q_UNUSED( value )
1294 if ( attributeChanged )
1295 {
1296 mFeatureListView->setCurrentFeatureEdited( true );
1297 mAttributeForm->save();
1298 }
1299}
1300
1302{
1303 mFilterModel->setFilteredFeatures( filteredFeatures );
1304}
1305
1306void QgsDualView::filterFeatures( const QgsExpression &filterExpression, const QgsExpressionContext &context )
1307{
1308 mFilterModel->setFilterExpression( filterExpression, context );
1309 mFilterModel->filterFeatures();
1310}
1311
1312
1314{
1315 mMasterModel->setRequest( request );
1316}
1317
1319{
1320 mTableView->setFeatureSelectionManager( featureSelectionManager );
1321 mFeatureListView->setFeatureSelectionManager( featureSelectionManager );
1322
1323 if ( mFeatureSelectionManager && mFeatureSelectionManager->parent() == this )
1324 delete mFeatureSelectionManager;
1325
1326 mFeatureSelectionManager = featureSelectionManager;
1327}
1328
1330{
1331 mConfig = config;
1332 mConfig.update( mLayer->fields() );
1333 mLayer->setAttributeTableConfig( mConfig );
1334 mFilterModel->setAttributeTableConfig( mConfig );
1335 mTableView->setAttributeTableConfig( mConfig );
1336 const QgsAttributeList attributes { requiredAttributes( mLayer ) };
1337 QgsFeatureRequest request = mMasterModel->request();
1338 // if the sort expression needs geometry reset the flag
1339 if ( QgsExpression( config.sortExpression() ).needsGeometry() )
1340 {
1341 mLayerCache->setCacheGeometry( true );
1342 request.setFlags( request.flags().setFlag( Qgis::FeatureRequestFlag::NoGeometry, false ) );
1343 }
1344 request.setSubsetOfAttributes( attributes );
1345 mMasterModel->setRequest( request );
1346 mLayerCache->setCacheSubsetOfAttributes( attributes );
1347}
1348
1349void QgsDualView::setSortExpression( const QString &sortExpression, Qt::SortOrder sortOrder )
1350{
1351
1352 if ( sortExpression.isNull() )
1353 mFilterModel->sort( -1 );
1354 else
1355 mFilterModel->sort( sortExpression, sortOrder );
1356
1358 mConfig.setSortOrder( sortOrder );
1359 setAttributeTableConfig( mConfig );
1360}
1361
1363{
1364 return mFilterModel->sortExpression();
1365}
1366
1368{
1369 return mConfig;
1370}
1371
1372void QgsDualView::progress( int i, bool &cancel )
1373{
1374 if ( !mProgressDlg )
1375 {
1376 mProgressDlg = new QProgressDialog( tr( "Loading features…" ), tr( "Abort" ), 0, 0, this );
1377 mProgressDlg->setWindowTitle( tr( "Attribute Table" ) );
1378 mProgressDlg->setWindowModality( Qt::WindowModal );
1379 mProgressDlg->show();
1380 }
1381
1382 mProgressDlg->setLabelText( tr( "%L1 features loaded." ).arg( i ) );
1383 QCoreApplication::processEvents();
1384
1385 cancel = mProgressDlg && mProgressDlg->wasCanceled();
1386}
1387
1388void QgsDualView::finished()
1389{
1390 delete mProgressDlg;
1391 mProgressDlg = nullptr;
1392}
1393
1394/*
1395 * QgsAttributeTableAction
1396 */
1397
1399{
1400 mDualView->masterModel()->executeAction( mAction, mFieldIdx );
1401}
1402
1404{
1405 QgsFeatureIds editedIds;
1406 editedIds << mDualView->masterModel()->rowToId( mFieldIdx.row() );
1407 mDualView->setCurrentEditSelection( editedIds );
1409}
1410
1411/*
1412 * QgsAttributeTableMapLayerAction
1413 */
1414
1416{
1418 mDualView->masterModel()->executeMapLayerAction( mAction, mFieldIdx, context );
1419}
@ NoFilter
No filter is applied.
@ NoGeometry
Geometry is not required. It may still be returned if e.g. required for a filter condition.
@ NoFilter
No spatial filtering of features.
@ MultipleFeatures
Action targets multiple features from a layer.
@ Layer
Action targets a complete layer.
@ SingleFeature
Action targets a single feature from a layer.
@ Expression
Field is calculated from an expression.
This class is a menu that is populated automatically with the actions defined for a given layer.
Utility class that encapsulates an action based on vector attributes.
Definition qgsaction.h:37
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.
QgsMessageBar * mainMessageBar()
Returns the main message bar.
@ SearchMode
Form values are used for searching/filtering the layer.
@ SingleEditMode
Single edit mode, for editing a single feature.
@ MultiEditMode
Multi edit mode, for editing fields of multiple features at once.
const QgsAttributeEditorContext * parentContext() const
void parentFormValueChanged(const QString &attribute, const QVariant &newValue)
Is called in embedded forms when an attribute value in the parent form has changed to newValue.
void setFeature(const QgsFeature &feature)
Update all editors to correspond to a different feature.
void refreshFeature()
reload current feature
bool needsGeometry() const
Returns true if any of the form widgets need feature geometry.
bool save()
Save all the values from the editors to the layer.
void filterExpressionSet(const QString &expression, QgsAttributeForm::FilterType type)
Emitted when a filter expression is set using the form.
void widgetValueChanged(const QString &attribute, const QVariant &value, bool attributeChanged)
Notifies about changes of attributes.
void flashFeatures(const QString &filter)
Emitted when the user chooses to flash a filtered set of features.
void modeChanged(QgsAttributeEditorContext::Mode mode)
Emitted when the form changes mode.
void zoomToFeatures(const QString &filter)
Emitted when the user chooses to zoom to a filtered set of features.
void setMode(QgsAttributeEditorContext::Mode mode)
Sets the current mode of the form.
This is a container for configuration of the attribute table.
void setSortExpression(const QString &sortExpression)
Set the sort expression used for sorting.
@ Field
This column represents a field.
Qt::SortOrder sortOrder() const
Gets the sort order.
QVector< QgsAttributeTableConfig::ColumnConfig > columns() const
Gets the list with all columns and their configuration.
int mapVisibleColumnToIndex(int visibleColumn) const
Maps a visible column index to its original column index.
void update(const QgsFields &fields)
Update the configuration with the given fields.
void setSortOrder(Qt::SortOrder sortOrder)
Set the sort order.
int columnWidth(int column) const
Returns the width of a column, or -1 if column should use default width.
void setColumnHidden(int column, bool hidden)
Sets whether the specified column should be hidden.
QString sortExpression() const
Gets the expression used for sorting.
void setColumnWidth(int column, int width)
Sets the width of a column.
QString sortExpression() const
The expression which is used to sort the attribute table.
FilterMode filterMode()
The current filterModel.
void sort(int column, Qt::SortOrder order=Qt::AscendingOrder) override
Sort by the given column using the given order.
QgsMapCanvas * mapCanvas() const
Returns the map canvas.
void setFilterMode(FilterMode filterMode)
Set the filter mode the filter will use.
QString filterExpression() const
Returns the stored filter expression string.
void setAttributeTableConfig(const QgsAttributeTableConfig &config, bool force=false)
Set the attribute table configuration to control which fields are shown, in which order they are show...
void disconnectFilterModeConnections()
Disconnect the connections set for the current filterMode.
FilterMode
The filter mode defines how the rows should be filtered.
@ ShowFilteredList
Show only features whose ids are on the filter list. {.
@ ShowVisible
Show only visible features (depends on the map canvas)
@ ShowSelected
Show only selected features.
@ ShowInvalid
Show only features not respecting constraints (since QGIS 3.30)
@ ShowEdited
Show only features which have unsaved changes.
void filterError(const QString &errorMessage)
Emitted when an error occurred while filtering features.
void filterFeatures()
Updates the filtered features in the filter model.
void setFilterExpression(const QgsExpression &expression, const QgsExpressionContext &context)
Set the expression and the context to be stored in case of the features need to be filtered again (li...
virtual void setFilteredFeatures(const QgsFeatureIds &ids)
Specify a list of features, which the filter will accept.
QgsFeatureId rowToId(const QModelIndex &row)
Returns the feature id for a given model index.
void featuresFiltered()
Emitted when the filtering of the features has been done.
void visibleReloaded()
Emitted when the the visible features on extend are reloaded (the list is created)
QgsVectorLayer * layer() const
Returns the layer this filter acts on.
void setSelectedOnTop(bool selectedOnTop)
Changes the sort order of the features.
void sortColumnChanged(int column, Qt::SortOrder order)
Emitted whenever the sort column is changed.
A model backed by a QgsVectorLayerCache which is able to provide feature/attribute information to a Q...
const QgsFeatureRequest & request() const
Gets the the feature request.
void fieldConditionalStyleChanged(const QString &fieldName)
Handles updating the model when the conditional style for a field changes.
void modelChanged()
Model has been changed.
void executeMapLayerAction(QgsMapLayerAction *action, const QModelIndex &idx, const QgsMapLayerActionContext &context=QgsMapLayerActionContext()) const
Execute a QgsMapLayerAction.
void progress(int i, bool &cancel)
void setRequest(const QgsFeatureRequest &request)
Set a request that will be used to fill this attribute table model.
int rowCount(const QModelIndex &parent=QModelIndex()) const override
Returns the number of rows.
void setEditorContext(const QgsAttributeEditorContext &context)
Sets the context in which this table is shown.
void setShowValidityState(bool show)
Sets whether the attribute table will add a visual feedback to cells when an attribute constraint is ...
QgsFeatureId rowToId(int row) const
Maps row to feature id.
virtual void loadLayer()
Loads the layer into the model Preferably to be called, before using this model as source for any oth...
void setExtraColumns(int extraColumns)
Empty extra columns to announce from this model.
void executeAction(QUuid action, const QModelIndex &idx) const
Execute an action.
QVariant data(const QModelIndex &index, int role) const override
Returns data on the given index.
void willShowContextMenu(QMenu *menu, const QModelIndex &atIndex)
Emitted in order to provide a hook to add additional* menu entries to the context menu.
void columnResized(int column, int width)
Emitted when a column in the view has been resized.
void showContextMenuExternally(QgsActionMenu *menu, QgsFeatureId fid)
Emitted when selecting context menu on the feature list to create the context menu individually.
void copyCellContent() const
Copy the content of the selected cell in the clipboard.
ViewMode
The view modes, in which this widget can present information.
Definition qgsdualview.h:56
@ 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.
~QgsDualView() override
static QgsAttributeList requiredAttributes(const QgsVectorLayer *layer)
Returns the list of required attributes according to the attribute table configuration of the layer,...
QgsAttributeTableFilterModel::FilterMode filterMode()
Gets the filter mode.
void setMultiEditEnabled(bool enabled)
Sets whether multi edit mode is enabled.
QgsFeatureIds filteredFeatures()
Gets a list of currently visible feature ids.
void cancelProgress()
Cancel the progress dialog (if any)
void filterChanged()
Emitted whenever the filter changes.
QgsDualView(QWidget *parent=nullptr)
Constructor.
void setAttributeTableConfig(const QgsAttributeTableConfig &config)
Set the attribute table config which should be used to control the appearance of the attribute table.
ViewMode view() const
Returns the current view mode.
int featureCount()
Returns the number of features on the layer.
Q_DECL_DEPRECATED void setFilteredFeatures(const QgsFeatureIds &filteredFeatures)
Set a list of currently visible features.
void formModeChanged(QgsAttributeEditorContext::Mode mode)
Emitted when the form changes mode.
FeatureListBrowsingAction
Action on the map canvas when browsing the list of features.
Definition qgsdualview.h:75
@ NoAction
No action is done.
Definition qgsdualview.h:76
@ PanToFeature
The map is panned to the center of the feature bounding-box.
Definition qgsdualview.h:77
@ ZoomToFeature
The map is zoomed to contained the feature bounding-box.
Definition qgsdualview.h:78
void hideEvent(QHideEvent *event) override
QgsAttributeTableConfig attributeTableConfig() const
The config used for the attribute table.
void filterExpressionSet(const QString &expression, QgsAttributeForm::FilterType type)
Emitted when a filter expression is set using the view.
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.
bool saveEditChanges()
saveEditChanges
void openConditionalStyles()
void toggleSearchMode(bool enabled)
Toggles whether search mode should be enabled in the form.
void displayExpressionChanged(const QString &expression)
Emitted whenever the display expression is successfully changed.
void setSortExpression(const QString &sortExpression, Qt::SortOrder sortOrder=Qt::AscendingOrder)
Set the expression used for sorting the table and feature list.
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 setCurrentEditSelection(const QgsFeatureIds &fids)
Set the current edit selection in the AttributeEditor mode.
int filteredFeatureCount()
Returns the number of features which are currently visible, according to the filter restrictions.
QString sortExpression() const
Gets the expression used for sorting the table and feature list.
void setFilterMode(QgsAttributeTableFilterModel::FilterMode filterMode)
Set the filter mode.
void setView(ViewMode view)
Change the current view mode.
void setSelectedOnTop(bool selectedOnTop)
Toggle the selectedOnTop flag.
void filterFeatures(const QgsExpression &filterExpression, const QgsExpressionContext &context)
Sets the expression and Updates the filtered features in the filter model.
A generic dialog for building expression strings.
A reusable widget that can be used to build a expression string.
QString expressionText()
Gets the expression string that has been set in the expression area.
void setExpressionText(const QString &expression)
Sets the expression string for the widget.
void initWithLayer(QgsVectorLayer *layer, const QgsExpressionContext &context=QgsExpressionContext(), const QString &recentCollection=QStringLiteral("generic"), QgsExpressionBuilderWidget::Flags flags=LoadAll)
Initialize with a layer.
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...
Class for parsing and evaluation of expressions (formerly called "search strings").
bool needsGeometry() const
Returns true if the expression uses feature geometry for some computation.
QSet< int > referencedAttributeIndexes(const QgsFields &fields) const
Returns a list of field name indexes obtained from the provided fields.
int rowCount(const QModelIndex &parent=QModelIndex()) const override
void setSortByDisplayExpression(bool sortByDisplayExpression, Qt::SortOrder order=Qt::AscendingOrder)
Sort this model by its display expression.
QVariant data(const QModelIndex &index, int role) const override
Shows a list of features and renders a edit button next to each feature.
void currentEditSelectionProgressChanged(int progress, int count)
Emitted whenever the current edit selection has been changed.
void editNextFeature()
editNextFeature will try to edit next feature of the list
void editLastFeature()
editLastFeature will try to edit the last feature of the list
void editFirstFeature()
editFirstFeature will try to edit the first feature of the list
void displayExpressionChanged(const QString &expression)
Emitted whenever the display expression is successfully changed.
void editPreviousFeature()
editPreviousFeature will try to edit previous feature of the list
void willShowContextMenu(QgsActionMenu *menu, const QModelIndex &atIndex)
Emitted when the context menu is created to add the specific actions to it.
void currentEditSelectionChanged(QgsFeature &feat)
Emitted whenever the current edit selection has been changed.
void aboutToChangeEditSelection(bool &ok)
This class wraps a request for features to a vector layer (or directly its vector data provider).
QgsFeatureRequest & setFlags(Qgis::FeatureRequestFlags flags)
Sets flags that affect how features will be fetched.
QgsRectangle filterRect() const
Returns the rectangle from which features will be taken.
QgsFeatureRequest & setFilterFids(const QgsFeatureIds &fids)
Sets the feature IDs that should be fetched.
Qgis::FeatureRequestFilterType filterType() const
Returns the attribute/ID filter type which is currently set on this request.
Qgis::FeatureRequestFlags flags() const
Returns the flags which affect how features are fetched.
QgsFeatureRequest & disableFilter()
Disables any attribute/ID filtering.
QgsFeatureRequest & setSubsetOfAttributes(const QgsAttributeList &attrs)
Set a subset of attributes that will be fetched.
QgsFeatureRequest & setFilterExpression(const QString &expression)
Set the filter expression.
Qgis::SpatialFilterType spatialFilterType() const
Returns the spatial filter type which is currently set on this request.
QgsFeatureRequest & setFilterRect(const QgsRectangle &rectangle)
Sets the rectangle from which features will be taken.
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
A widget for customizing conditional formatting options.
void rulesUpdated(const QString &fieldName)
Emitted when the conditional styling rules are updated.
void setLayer(QgsVectorLayer *layer)
Sets the vector layer associated with the widget.
Encapsulate a field in an attribute table or data source.
Definition qgsfield.h:53
Qgis::FieldOrigin fieldOrigin(int fieldIdx) const
Returns the field's origin (value from an enumeration).
Q_INVOKABLE int lookupField(const QString &fieldName) const
Looks up field's index from the field name.
static QgsEditorWidgetRegistry * editorWidgetRegistry()
Returns the global editor widget registry, used for managing all known edit widget factories.
Definition qgsgui.cpp:89
static QgsShortcutsManager * shortcutsManager()
Returns the global shortcuts manager, used for managing a QAction and QShortcut sequences.
Definition qgsgui.cpp:119
static QgsMapLayerActionRegistry * mapLayerActionRegistry()
Returns the global map layer action registry, used for registering map layer actions.
Definition qgsgui.cpp:129
Is an interface class to abstract feature selection handling.
Map canvas is a class for displaying all GIS data types on a canvas.
void extentsChanged()
Emitted when the extents of the map change.
void panToFeatureIds(QgsVectorLayer *layer, const QgsFeatureIds &ids, bool alwaysRecenter=true)
Centers canvas extent to feature ids.
void flashFeatureIds(QgsVectorLayer *layer, const QgsFeatureIds &ids, const QColor &startColor=QColor(255, 0, 0, 255), const QColor &endColor=QColor(255, 0, 0, 0), int flashes=3, int duration=500)
Causes a set of features with matching ids from a vector layer to flash within the canvas.
void zoomToFeatureIds(QgsVectorLayer *layer, const QgsFeatureIds &ids)
Set canvas extent to the bounding box of a set of features.
const QgsMapSettings & mapSettings() const
Gets access to properties used for map rendering.
QgsRectangle extent() const
Returns the current zoom extent of the map canvas.
Encapsulates the context in which a QgsMapLayerAction action is executed.
QList< QgsMapLayerAction * > mapLayerActions(QgsMapLayer *layer, Qgis::MapLayerActionTargets targets=Qgis::MapLayerActionTarget::AllActions, const QgsMapLayerActionContext &context=QgsMapLayerActionContext())
Returns the map layer actions which can run on the specified layer.
An action which can run on map layers The class can be used in two manners:
void layerModified()
Emitted when modifications has been done on layer.
QgsPointXY mapToLayerCoordinates(const QgsMapLayer *layer, QgsPointXY point) const
transform point coordinates from output CRS to layer's CRS
void pushWarning(const QString &title, const QString &message)
Pushes a warning message that must be manually dismissed by the user.
Dialog for organising (hiding and reordering) columns in the attributes table.
virtual void setDockMode(bool dockMode)
Set the widget in dock mode which tells the widget to emit panel widgets and not open dialogs.
A rectangle specified with double values.
bool isNull() const
Test if the rectangle is null (holding no spatial information).
A QScrollArea subclass with improved scrolling behavior.
This class is a composition of two QSettings instances:
Definition qgssettings.h:64
QVariant value(const QString &key, const QVariant &defaultValue=QVariant(), Section section=NoSection) const
Returns the value for setting key.
void setEnumValue(const QString &key, const T &value, const Section section=NoSection)
Set the value of a setting based on an enum.
void setValue(const QString &key, const QVariant &value, QgsSettings::Section section=QgsSettings::NoSection)
Sets the value of setting key to value.
T enumValue(const QString &key, const T &defaultValue, const Section section=NoSection)
Returns the setting value for a setting based on an enum.
QShortcut * shortcutByName(const QString &name) const
Returns a shortcut by its name, or nullptr if nothing found.
@ SelectAtId
Fast access to features using their ID.
This class caches features of a given QgsVectorLayer.
void setFullCache(bool fullCache)
This enables or disables full caching.
void finished()
When filling the cache, this signal gets emitted once the cache is fully initialized.
void invalidated()
The cache has been invalidated and cleared.
void setCacheSubsetOfAttributes(const QgsAttributeList &attributes)
Set the list (possibly a subset) of attributes to be cached.
void progress(int i, bool &cancel)
When filling the cache, this signal gets emitted periodically to notify about the progress and to be ...
void setCacheGeometry(bool cacheGeometry)
Enable or disable the caching of geometries.
Represents a vector layer which manages a vector based data sets.
QString expressionField(int index) const
Returns the expression used for a given expression field.
void afterCommitChanges()
Emitted after changes are committed to the data provider.
QgsAttributeTableConfig attributeTableConfig() const
Returns the attribute table configuration object.
void selectionChanged(const QgsFeatureIds &selected, const QgsFeatureIds &deselected, bool clearAndSelect)
Emitted when selection was changed.
void updatedFields()
Emitted whenever the fields available from this layer have been changed.
QgsSignalBlocker< Object > whileBlocking(Object *object)
Temporarily blocks signals from a QObject while calling a single method from the object.
Definition qgis.h:5369
QSet< QgsFeatureId > QgsFeatureIds
qint64 QgsFeatureId
64 bit feature ids negative numbers are used for uncommitted/newly added features
QList< int > QgsAttributeList
Definition qgsfield.h:27
Defines the configuration of a column in the attribute table.