QGIS API Documentation 3.41.0-Master (af5edcb665c)
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"
30#include "moc_qgsdualview.cpp"
32#include "qgsfeaturelistmodel.h"
34#include "qgsmapcanvas.h"
35#include "qgsmaplayeraction.h"
37#include "qgsvectorlayercache.h"
40#include "qgssettings.h"
41#include "qgsscrollarea.h"
42#include "qgsgui.h"
44#include "qgsshortcutsmanager.h"
46#include "qgsmapcanvasutils.h"
47#include "qgsmessagebar.h"
49#include "qgsactionmenu.h"
50
51const std::unique_ptr<QgsSettingsEntryVariant> QgsDualView::conditionalFormattingSplitterState = std::make_unique<QgsSettingsEntryVariant>( QStringLiteral( "attribute-table-splitter-state" ), QgsSettingsTree::sTreeWindowState, QgsVariantUtils::createNullVariant( QMetaType::Type::QByteArray ), QStringLiteral( "State of conditionnal formatting splitter's layout so it could be restored when opening attribute table view." ) );
52const std::unique_ptr<QgsSettingsEntryVariant> QgsDualView::attributeEditorSplitterState = std::make_unique<QgsSettingsEntryVariant>( QStringLiteral( "attribute-editor-splitter-state" ), QgsSettingsTree::sTreeWindowState, QgsVariantUtils::createNullVariant( QMetaType::Type::QByteArray ), QStringLiteral( "State of attribute editor splitter's layout so it could be restored when opening attribute editor view." ) );
53
54QgsDualView::QgsDualView( QWidget *parent )
55 : QStackedWidget( parent )
56{
57 setupUi( this );
58 connect( mFeatureListView, &QgsFeatureListView::aboutToChangeEditSelection, this, &QgsDualView::featureListAboutToChangeEditSelection );
59 connect( mFeatureListView, &QgsFeatureListView::currentEditSelectionChanged, this, &QgsDualView::featureListCurrentEditSelectionChanged );
60 connect( mFeatureListView, &QgsFeatureListView::currentEditSelectionProgressChanged, this, &QgsDualView::updateEditSelectionProgress );
61 connect( mFeatureListView, &QgsFeatureListView::willShowContextMenu, this, &QgsDualView::widgetWillShowContextMenu );
62
63 connect( mTableView, &QgsAttributeTableView::willShowContextMenu, this, &QgsDualView::viewWillShowContextMenu );
64 mTableView->horizontalHeader()->setContextMenuPolicy( Qt::CustomContextMenu );
65 connect( mTableView->horizontalHeader(), &QHeaderView::customContextMenuRequested, this, &QgsDualView::showViewHeaderMenu );
66 connect( mTableView, &QgsAttributeTableView::columnResized, this, &QgsDualView::tableColumnResized );
67
68 mConditionalFormatWidgetStack->hide();
69 mConditionalFormatWidget = new QgsFieldConditionalFormatWidget( this );
70 mConditionalFormatWidgetStack->setMainPanel( mConditionalFormatWidget );
71 mConditionalFormatWidget->setDockMode( true );
72
73 const QgsSettings settings;
74 // copy old setting
75 conditionalFormattingSplitterState->copyValueFromKey( QStringLiteral( "/qgis/attributeTable/splitterState" ), true );
76 mConditionalSplitter->restoreState( conditionalFormattingSplitterState->value().toByteArray() );
77 mAttributeEditorViewSplitter->restoreState( attributeEditorSplitterState->value().toByteArray() );
78
79 mPreviewColumnsMenu = new QMenu( this );
80 mActionPreviewColumnsMenu->setMenu( mPreviewColumnsMenu );
81
82 // Set preview icon
83 mActionExpressionPreview->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mIconExpressionPreview.svg" ) ) );
84
85 // Connect layer list preview signals
86 connect( mActionExpressionPreview, &QAction::triggered, this, &QgsDualView::previewExpressionBuilder );
87 connect( mFeatureListView, &QgsFeatureListView::displayExpressionChanged, this, &QgsDualView::previewExpressionChanged );
88
89 // browsing toolbar
90 connect( mNextFeatureButton, &QToolButton::clicked, mFeatureListView, &QgsFeatureListView::editNextFeature );
91 connect( mPreviousFeatureButton, &QToolButton::clicked, mFeatureListView, &QgsFeatureListView::editPreviousFeature );
92 connect( mFirstFeatureButton, &QToolButton::clicked, mFeatureListView, &QgsFeatureListView::editFirstFeature );
93 connect( mLastFeatureButton, &QToolButton::clicked, mFeatureListView, &QgsFeatureListView::editLastFeature );
94
95 auto createShortcuts = [this]( const QString &objectName, void ( QgsFeatureListView::*slot )() ) {
96 QShortcut *sc = QgsGui::shortcutsManager()->shortcutByName( objectName );
97 // do not assert for sc as it would lead to crashes in testing
98 // or when using custom widgets lib if built with Debug
99 if ( sc )
100 connect( sc, &QShortcut::activated, mFeatureListView, slot );
101 };
102 createShortcuts( QStringLiteral( "mAttributeTableFirstEditedFeature" ), &QgsFeatureListView::editFirstFeature );
103 createShortcuts( QStringLiteral( "mAttributeTablePreviousEditedFeature" ), &QgsFeatureListView::editPreviousFeature );
104 createShortcuts( QStringLiteral( "mAttributeTableNextEditedFeature" ), &QgsFeatureListView::editNextFeature );
105 createShortcuts( QStringLiteral( "mAttributeTableLastEditedFeature" ), &QgsFeatureListView::editLastFeature );
106
107 QButtonGroup *buttonGroup = new QButtonGroup( this );
108 buttonGroup->setExclusive( false );
109 buttonGroup->addButton( mAutoPanButton, PanToFeature );
110 buttonGroup->addButton( mAutoZoomButton, ZoomToFeature );
111 const FeatureListBrowsingAction action = QgsSettings().enumValue( QStringLiteral( "/qgis/attributeTable/featureListBrowsingAction" ), NoAction );
112 QAbstractButton *bt = buttonGroup->button( static_cast<int>( action ) );
113 if ( bt )
114 bt->setChecked( true );
115 connect( buttonGroup, qOverload<QAbstractButton *, bool>( &QButtonGroup::buttonToggled ), this, &QgsDualView::panZoomGroupButtonToggled );
116 mFlashButton->setChecked( QgsSettings().value( QStringLiteral( "/qgis/attributeTable/featureListHighlightFeature" ), true ).toBool() );
117 connect( mFlashButton, &QToolButton::clicked, this, &QgsDualView::flashButtonClicked );
118}
119
123
124void QgsDualView::init( QgsVectorLayer *layer, QgsMapCanvas *mapCanvas, const QgsFeatureRequest &request, const QgsAttributeEditorContext &context, bool loadFeatures, bool showFirstFeature )
125{
126 if ( !layer )
127 return;
128
129 delete mAttributeForm;
130 mAttributeForm = nullptr;
131
132 mLayer = layer;
133
134 // Keep fields order in sync: force config reset
135 connect( mLayer, &QgsVectorLayer::updatedFields, this, [this] {
136 mFilterModel->setAttributeTableConfig( attributeTableConfig(), /* force */ true );
137 } );
138
139 mEditorContext = context;
140
141 // create an empty form to find out if it needs geometry or not
142 const QgsAttributeForm emptyForm( mLayer, QgsFeature(), mEditorContext );
143
144 const QgsExpression sortingExpression = QgsExpression( mConfig.sortExpression() );
145
146 const bool needsGeometry = mLayer->conditionalStyles()->rulesNeedGeometry() || !( request.flags() & Qgis::FeatureRequestFlag::NoGeometry )
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, [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, [this]( const QString &filter ) {
201 if ( QgsMapCanvas *canvas = mFilterModel->mapCanvas() )
202 {
203 QgsMapCanvasUtils::flashMatchingFeatures( canvas, mLayer, filter );
204 }
205 } );
206 connect( mAttributeForm, &QgsAttributeForm::zoomToFeatures, this, [this]( const QString &filter ) {
207 if ( QgsMapCanvas *canvas = mFilterModel->mapCanvas() )
208 {
209 QgsMapCanvasUtils::zoomToMatchingFeatures( canvas, mLayer, filter );
210 }
211 } );
212
213 connect( mMasterModel, &QgsAttributeTableModel::modelChanged, mAttributeForm, &QgsAttributeForm::refreshFeature );
214}
215
216void QgsDualView::columnBoxInit()
217{
218 // load fields
219 const QList<QgsField> fields = mLayer->fields().toList();
220
221 const QString defaultField;
222
223 mFeatureListPreviewButton->addAction( mActionExpressionPreview );
224 mFeatureListPreviewButton->addAction( mActionPreviewColumnsMenu );
225
226 const auto constFields = fields;
227 for ( const QgsField &field : constFields )
228 {
229 const int fieldIndex = mLayer->fields().lookupField( field.name() );
230 if ( fieldIndex == -1 )
231 continue;
232
233 const QString fieldName = field.name();
234 if ( QgsGui::editorWidgetRegistry()->findBest( mLayer, fieldName ).type() != QLatin1String( "Hidden" ) )
235 {
236 const QIcon icon = mLayer->fields().iconForField( fieldIndex );
237 const QString text = mLayer->attributeDisplayName( fieldIndex );
238
239 // Generate action for the preview popup button of the feature list
240 QAction *previewAction = new QAction( icon, text, mFeatureListPreviewButton );
241 connect( previewAction, &QAction::triggered, this, [this, previewAction, fieldName] { previewColumnChanged( previewAction, fieldName ); } );
242 mPreviewColumnsMenu->addAction( previewAction );
243
244 if ( text == defaultField )
245 {
246 mFeatureListPreviewButton->setDefaultAction( previewAction );
247 }
248 }
249 }
250
251 QMenu *sortMenu = new QMenu( this );
252 QAction *sortMenuAction = new QAction( QgsApplication::getThemeIcon( QStringLiteral( "sort.svg" ) ), tr( "Sort…" ), this );
253 sortMenuAction->setMenu( sortMenu );
254
255 QAction *sortByPreviewExpressionAsc = new QAction( QgsApplication::getThemeIcon( QStringLiteral( "sort.svg" ) ), tr( "By Preview Expression (ascending)" ), this );
256 connect( sortByPreviewExpressionAsc, &QAction::triggered, this, [this]() {
257 mFeatureListModel->setSortByDisplayExpression( true, Qt::AscendingOrder );
258 } );
259 sortMenu->addAction( sortByPreviewExpressionAsc );
260 QAction *sortByPreviewExpressionDesc = new QAction( QgsApplication::getThemeIcon( QStringLiteral( "sort-reverse.svg" ) ), tr( "By Preview Expression (descending)" ), this );
261 connect( sortByPreviewExpressionDesc, &QAction::triggered, this, [this]() {
262 mFeatureListModel->setSortByDisplayExpression( true, Qt::DescendingOrder );
263 } );
264 sortMenu->addAction( sortByPreviewExpressionDesc );
265 QAction *sortByPreviewExpressionCustom = new QAction( QgsApplication::getThemeIcon( QStringLiteral( "mIconExpressionPreview.svg" ) ), tr( "By Custom Expression" ), this );
266 connect( sortByPreviewExpressionCustom, &QAction::triggered, this, [this]() {
267 if ( modifySort() )
268 mFeatureListModel->setSortByDisplayExpression( false );
269 } );
270 sortMenu->addAction( sortByPreviewExpressionCustom );
271
272 mFeatureListPreviewButton->addAction( sortMenuAction );
273
274 QAction *separator = new QAction( mFeatureListPreviewButton );
275 separator->setSeparator( true );
276 mFeatureListPreviewButton->addAction( separator );
277 restoreRecentDisplayExpressions();
278
279 // If there is no single field found as preview
280 if ( !mFeatureListPreviewButton->defaultAction() )
281 {
282 mFeatureListView->setDisplayExpression( mLayer->displayExpression() );
283 mFeatureListPreviewButton->setDefaultAction( mActionExpressionPreview );
284 const QString displayExpression = mFeatureListView->displayExpression();
285 setDisplayExpression( displayExpression.isEmpty() ? tr( "'[Please define preview text]'" ) : displayExpression );
286 }
287 else
288 {
289 mFeatureListPreviewButton->defaultAction()->trigger();
290 }
291}
292
294{
295 setCurrentIndex( view );
296}
297
299{
300 return static_cast<QgsDualView::ViewMode>( currentIndex() );
301}
302
304{
305 // cleanup any existing connections
306 switch ( mFilterModel->filterMode() )
307 {
309 disconnect( mFilterModel->mapCanvas(), &QgsMapCanvas::extentsChanged, this, &QgsDualView::extentChanged );
311 break;
312
316 disconnect( mFilterModel, &QgsAttributeTableFilterModel::filterError, this, &QgsDualView::filterError );
317 break;
318
321 disconnect( mFilterModel, &QgsAttributeTableFilterModel::filterError, this, &QgsDualView::filterError );
322 disconnect( masterModel()->layer(), &QgsVectorLayer::layerModified, this, &QgsDualView::updateEditedAddedFeatures );
323 break;
324
326 disconnect( masterModel()->layer(), &QgsVectorLayer::selectionChanged, this, &QgsDualView::updateSelectedFeatures );
327 break;
328
330 mMasterModel->setShowValidityState( false );
331 break;
332 }
333
334 QgsFeatureRequest request = mMasterModel->request();
335
336 // create an empty form to find out if it needs geometry or not
337 const QgsAttributeForm emptyForm( mLayer, QgsFeature(), mEditorContext );
338 const bool needsGeometry = ( filterMode == QgsAttributeTableFilterModel::ShowVisible ) || emptyForm.needsGeometry() || QgsExpression( mConfig.sortExpression() ).needsGeometry();
339
340 const bool requiresTableReload = ( request.filterType() != Qgis::Qgis::FeatureRequestFilterType::NoFilter || request.spatialFilterType() != Qgis::SpatialFilterType::NoFilter ) // previous request was subset
341 || ( needsGeometry && request.flags() & Qgis::FeatureRequestFlag::NoGeometry ) // no geometry for last request
342 || ( mMasterModel->rowCount() == 0 ); // no features
343
344 request.setFlags( request.flags().setFlag( Qgis::FeatureRequestFlag::NoGeometry, !needsGeometry ) );
345 request.setFilterFids( QgsFeatureIds() );
346 request.setFilterRect( QgsRectangle() );
347 request.disableFilter();
348
349 // setup new connections and filter request parameters
350 switch ( filterMode )
351 {
353 connect( mFilterModel->mapCanvas(), &QgsMapCanvas::extentsChanged, this, &QgsDualView::extentChanged );
354 if ( mFilterModel->mapCanvas() )
355 {
356 const QgsRectangle rect = mFilterModel->mapCanvas()->mapSettings().mapToLayerCoordinates( mLayer, mFilterModel->mapCanvas()->extent() );
357 request.setFilterRect( rect );
358 }
360 break;
361
363 request.setFilterFids( masterModel()->layer()->editBuffer() ? masterModel()->layer()->editBuffer()->allAddedOrEditedFeatures() : QgsFeatureIds() );
365 connect( mFilterModel, &QgsAttributeTableFilterModel::filterError, this, &QgsDualView::filterError );
366 connect( masterModel()->layer(), &QgsVectorLayer::layerModified, this, &QgsDualView::updateEditedAddedFeatures );
367 break;
368
370 {
371 mMasterModel->setShowValidityState( true );
373 filterFeatures( QStringLiteral( "is_feature_valid() = false" ), context );
375 connect( mFilterModel, &QgsAttributeTableFilterModel::filterError, this, &QgsDualView::filterError );
376 break;
377 }
378
381 {
382 const QString filterExpression = filterMode == QgsAttributeTableFilterModel::ShowFilteredList ? mFilterModel->filterExpression() : QString();
383 if ( !filterExpression.isEmpty() )
384 request.setFilterExpression( mFilterModel->filterExpression() );
386 connect( mFilterModel, &QgsAttributeTableFilterModel::filterError, this, &QgsDualView::filterError );
387 break;
388 }
389
391 connect( masterModel()->layer(), &QgsVectorLayer::selectionChanged, this, &QgsDualView::updateSelectedFeatures );
392 request.setFilterFids( masterModel()->layer()->selectedFeatureIds() );
393 break;
394 }
395
396 // disable the browsing auto pan/scale if the list only shows visible items
397 switch ( filterMode )
398 {
400 setBrowsingAutoPanScaleAllowed( false );
401 break;
402
408 setBrowsingAutoPanScaleAllowed( true );
409 break;
410 }
411
412 if ( requiresTableReload )
413 {
414 //disconnect the connections of the current (old) filtermode before reload
415 mFilterModel->disconnectFilterModeConnections();
416
417 mMasterModel->setRequest( request );
418 whileBlocking( mLayerCache )->setCacheGeometry( needsGeometry );
419 mMasterModel->loadLayer();
420 }
421
422 //update filter model
423 mFilterModel->setFilterMode( filterMode );
424 emit filterChanged();
425}
426
427void QgsDualView::setSelectedOnTop( bool selectedOnTop )
428{
429 mFilterModel->setSelectedOnTop( selectedOnTop );
430}
431
432void QgsDualView::initLayerCache( bool cacheGeometry )
433{
434 // Initialize the cache
435 const QgsSettings settings;
436 const int cacheSize = settings.value( QStringLiteral( "qgis/attributeTableRowCache" ), "10000" ).toInt();
437 mLayerCache = new QgsVectorLayerCache( mLayer, cacheSize, this );
438 mLayerCache->setCacheSubsetOfAttributes( requiredAttributes( mLayer ) );
439 mLayerCache->setCacheGeometry( cacheGeometry );
440 if ( 0 == cacheSize || !mLayer->dataProvider()->capabilities().testFlag( Qgis::VectorProviderCapability::SelectAtId ) )
441 {
442 connect( mLayerCache, &QgsVectorLayerCache::invalidated, this, &QgsDualView::rebuildFullLayerCache );
443 rebuildFullLayerCache();
444 }
445}
446
447void QgsDualView::initModels( QgsMapCanvas *mapCanvas, const QgsFeatureRequest &request, bool loadFeatures )
448{
449 delete mFeatureListModel;
450 delete mFilterModel;
451 delete mMasterModel;
452
453 mMasterModel = new QgsAttributeTableModel( mLayerCache, this );
454 mMasterModel->setRequest( request );
455 mMasterModel->setEditorContext( mEditorContext );
456 mMasterModel->setExtraColumns( 1 ); // Add one extra column which we can "abuse" as an action column
457
458 connect( mMasterModel, &QgsAttributeTableModel::progress, this, &QgsDualView::progress );
459 connect( mMasterModel, &QgsAttributeTableModel::finished, this, &QgsDualView::finished );
460
462
463 if ( loadFeatures )
464 mMasterModel->loadLayer();
465
466 mFilterModel = new QgsAttributeTableFilterModel( mapCanvas, mMasterModel, mMasterModel );
467
468 // The following connections to invalidate() are necessary to keep the filter model in sync
469 // see regression https://github.com/qgis/QGIS/issues/23890
470 connect( mMasterModel, &QgsAttributeTableModel::rowsRemoved, mFilterModel, &QgsAttributeTableFilterModel::invalidate );
471 connect( mMasterModel, &QgsAttributeTableModel::rowsInserted, mFilterModel, &QgsAttributeTableFilterModel::invalidate );
472
474
475 mFeatureListModel = new QgsFeatureListModel( mFilterModel, mFilterModel );
476}
477
478void QgsDualView::restoreRecentDisplayExpressions()
479{
480 const QVariantList previewExpressions = mLayer->customProperty( QStringLiteral( "dualview/previewExpressions" ) ).toList();
481
482 for ( const QVariant &previewExpression : previewExpressions )
483 insertRecentlyUsedDisplayExpression( previewExpression.toString() );
484}
485
486void QgsDualView::saveRecentDisplayExpressions() const
487{
488 if ( !mLayer )
489 {
490 return;
491 }
492 const QList<QAction *> actions = mFeatureListPreviewButton->actions();
493
494 // Remove existing same action
495 int index = actions.indexOf( mLastDisplayExpressionAction );
496 if ( index != -1 )
497 {
498 QVariantList previewExpressions;
499 for ( ; index < actions.length(); ++index )
500 {
501 QAction *action = actions.at( index );
502 previewExpressions << action->property( "previewExpression" );
503 }
504
505 mLayer->setCustomProperty( QStringLiteral( "dualview/previewExpressions" ), previewExpressions );
506 }
507}
508
509void QgsDualView::setDisplayExpression( const QString &expression )
510{
511 mDisplayExpression = expression;
512 insertRecentlyUsedDisplayExpression( expression );
513}
514
515void QgsDualView::insertRecentlyUsedDisplayExpression( const QString &expression )
516{
517 const QList<QAction *> actions = mFeatureListPreviewButton->actions();
518
519 // Remove existing same action
520 const int index = actions.indexOf( mLastDisplayExpressionAction );
521 if ( index != -1 )
522 {
523 for ( int i = 0; index + i < actions.length(); ++i )
524 {
525 QAction *action = actions.at( index );
526 if ( action->text() == expression || i >= 9 )
527 {
528 if ( action == mLastDisplayExpressionAction )
529 mLastDisplayExpressionAction = nullptr;
530 mFeatureListPreviewButton->removeAction( action );
531 }
532 else
533 {
534 if ( !mLastDisplayExpressionAction )
535 mLastDisplayExpressionAction = action;
536 }
537 }
538 }
539
540 QString name = expression;
541 QIcon icon = QgsApplication::getThemeIcon( QStringLiteral( "/mIconExpressionPreview.svg" ) );
542 if ( expression.startsWith( QLatin1String( "COALESCE( \"" ) ) && expression.endsWith( QLatin1String( ", '<NULL>' )" ) ) )
543 {
544 name = expression.mid( 11, expression.length() - 24 ); // Numbers calculated from the COALESCE / <NULL> parts
545
546 const int fieldIndex = mLayer->fields().indexOf( name );
547 if ( fieldIndex != -1 )
548 {
549 name = mLayer->attributeDisplayName( fieldIndex );
550 icon = mLayer->fields().iconForField( fieldIndex );
551 }
552 else
553 {
554 name = expression;
555 }
556 }
557
558 QAction *previewAction = new QAction( icon, name, mFeatureListPreviewButton );
559 previewAction->setProperty( "previewExpression", expression );
560 connect( previewAction, &QAction::triggered, this, [expression, this]( bool ) {
561 setDisplayExpression( expression );
562 mFeatureListPreviewButton->setText( expression );
563 } );
564
565 mFeatureListPreviewButton->insertAction( mLastDisplayExpressionAction, previewAction );
566 mLastDisplayExpressionAction = previewAction;
567}
568
569void QgsDualView::updateEditSelectionProgress( int progress, int count )
570{
571 mProgressCount->setText( QStringLiteral( "%1 / %2" ).arg( progress + 1 ).arg( count ) );
572 mPreviousFeatureButton->setEnabled( progress > 0 );
573 mNextFeatureButton->setEnabled( progress + 1 < count );
574 mFirstFeatureButton->setEnabled( progress > 0 );
575 mLastFeatureButton->setEnabled( progress + 1 < count );
576 if ( mAttributeForm )
577 {
578 mAttributeForm->setVisible( count > 0 );
579 }
580}
581
582void QgsDualView::panOrZoomToFeature( const QgsFeatureIds &featureset )
583{
584 QgsMapCanvas *canvas = mFilterModel->mapCanvas();
585 if ( canvas && view() == AttributeEditor && featureset != mLastFeatureSet )
586 {
587 if ( mBrowsingAutoPanScaleAllowed )
588 {
589 if ( mAutoPanButton->isChecked() )
590 QTimer::singleShot( 0, this, [this, featureset, canvas]() {
591 canvas->panToFeatureIds( mLayer, featureset, false );
592 } );
593 else if ( mAutoZoomButton->isChecked() )
594 QTimer::singleShot( 0, this, [this, featureset, canvas]() {
595 canvas->zoomToFeatureIds( mLayer, featureset );
596 } );
597 }
598 if ( mFlashButton->isChecked() )
599 QTimer::singleShot( 0, this, [this, featureset, canvas]() {
600 canvas->flashFeatureIds( mLayer, featureset );
601 } );
602 mLastFeatureSet = featureset;
603 }
604}
605
606void QgsDualView::setBrowsingAutoPanScaleAllowed( bool allowed )
607{
608 if ( mBrowsingAutoPanScaleAllowed == allowed )
609 return;
610
611 mBrowsingAutoPanScaleAllowed = allowed;
612
613 mAutoPanButton->setEnabled( allowed );
614 mAutoZoomButton->setEnabled( allowed );
615
616 const QString disabledHint = tr( "(disabled when attribute table only shows features visible in the current map canvas extent)" );
617
618 mAutoPanButton->setToolTip( tr( "Automatically pan to the current feature" ) + ( allowed ? QString() : QString( ' ' ) + disabledHint ) );
619 mAutoZoomButton->setToolTip( tr( "Automatically zoom to the current feature" ) + ( allowed ? QString() : QString( ' ' ) + disabledHint ) );
620}
621
622void QgsDualView::panZoomGroupButtonToggled( QAbstractButton *button, bool checked )
623{
624 if ( button == mAutoPanButton && checked )
625 {
626 QgsSettings().setEnumValue( QStringLiteral( "/qgis/attributeTable/featureListBrowsingAction" ), PanToFeature );
627 mAutoZoomButton->setChecked( false );
628 }
629 else if ( button == mAutoZoomButton && checked )
630 {
631 QgsSettings().setEnumValue( QStringLiteral( "/qgis/attributeTable/featureListBrowsingAction" ), ZoomToFeature );
632 mAutoPanButton->setChecked( false );
633 }
634 else
635 {
636 QgsSettings().setEnumValue( QStringLiteral( "/qgis/attributeTable/featureListBrowsingAction" ), NoAction );
637 }
638
639 if ( checked && mLayer->isSpatial() )
640 panOrZoomToFeature( mFeatureListView->currentEditSelection() );
641}
642
643void QgsDualView::flashButtonClicked( bool clicked )
644{
645 QgsSettings().setValue( QStringLiteral( "/qgis/attributeTable/featureListHighlightFeature" ), clicked );
646 if ( !clicked )
647 return;
648
649 QgsMapCanvas *canvas = mFilterModel->mapCanvas();
650
651 if ( canvas )
652 canvas->flashFeatureIds( mLayer, mFeatureListView->currentEditSelection() );
653}
654
655void QgsDualView::filterError( const QString &errorMessage )
656{
657 if ( mEditorContext.mainMessageBar() )
658 {
659 mEditorContext.mainMessageBar()->pushWarning( tr( "An error occurred while filtering features" ), errorMessage );
660 }
661}
662
663void QgsDualView::featureListAboutToChangeEditSelection( bool &ok )
664{
665 if ( !mAttributeForm )
666 return;
667
668 if ( mLayer->isEditable() && !mAttributeForm->save() )
669 ok = false;
670}
671
672void QgsDualView::featureListCurrentEditSelectionChanged( const QgsFeature &feat )
673{
674 if ( !mAttributeForm )
675 {
676 initAttributeForm( feat );
677 }
678 else if ( !mLayer->isEditable() || mAttributeForm->save() )
679 {
680 mAttributeForm->setFeature( feat );
681 QgsFeatureIds featureset;
682 featureset << feat.id();
683 setCurrentEditSelection( featureset );
684
685 if ( mLayer->isSpatial() )
686 panOrZoomToFeature( featureset );
687 }
688 else
689 {
690 // Couldn't save feature
691 }
692}
693
695{
696 mFeatureListView->setCurrentFeatureEdited( false );
697 mFeatureListView->setEditSelection( fids );
698}
699
701{
702 return mAttributeForm ? mAttributeForm->save() : false;
703}
704
706{
707 mConditionalFormatWidgetStack->setVisible( !mConditionalFormatWidgetStack->isVisible() );
708}
709
711{
712 if ( !mAttributeForm )
713 return;
714
715 if ( enabled )
716 {
717 mPreviousView = view();
719 }
720 else
721 {
722 setView( mPreviousView );
723 }
724
726}
727
729{
730 if ( !mAttributeForm )
731 return;
732
733 if ( enabled )
734 {
737 mAttributeForm->setVisible( true );
738 }
739 else
740 {
742 mAttributeForm->setVisible( mFilterModel->rowCount() > 0 );
743 }
744}
745
746void QgsDualView::previewExpressionBuilder()
747{
748 // Show expression builder
750
751 QgsExpressionBuilderDialog dlg( mLayer, mFeatureListView->displayExpression(), this, QStringLiteral( "generic" ), context );
752 dlg.setWindowTitle( tr( "Expression Based Preview" ) );
753 dlg.setExpressionText( mFeatureListView->displayExpression() );
754
755 if ( dlg.exec() == QDialog::Accepted )
756 {
757 mFeatureListView->setDisplayExpression( dlg.expressionText() );
758 mFeatureListPreviewButton->setDefaultAction( mActionExpressionPreview );
759 mFeatureListPreviewButton->setPopupMode( QToolButton::MenuButtonPopup );
760 }
761
762 setDisplayExpression( mFeatureListView->displayExpression() );
763}
764
765void QgsDualView::previewColumnChanged( QAction *previewAction, const QString &expression )
766{
767 if ( !mFeatureListView->setDisplayExpression( QStringLiteral( "COALESCE( \"%1\", '<NULL>' )" ).arg( expression ) ) )
768 {
769 QMessageBox::warning( this, tr( "Column Preview" ), tr( "Could not set column '%1' as preview column.\nParser error:\n%2" ).arg( previewAction->text(), mFeatureListView->parserErrorString() ) );
770 }
771 else
772 {
773 mFeatureListPreviewButton->setText( previewAction->text() );
774 mFeatureListPreviewButton->setIcon( previewAction->icon() );
775 mFeatureListPreviewButton->setPopupMode( QToolButton::InstantPopup );
776 }
777
778 setDisplayExpression( mFeatureListView->displayExpression() );
779}
780
782{
783 return mMasterModel->rowCount();
784}
785
787{
788 return mFilterModel->rowCount();
789}
790
792{
793 const QModelIndex currentIndex = mTableView->currentIndex();
794 if ( !currentIndex.isValid() )
795 {
796 return;
797 }
798
799 const QVariant var = mMasterModel->data( currentIndex, Qt::DisplayRole );
800 QApplication::clipboard()->setText( var.toString() );
801}
802
804{
805 if ( mProgressDlg )
806 mProgressDlg->cancel();
807}
808
809void QgsDualView::parentFormValueChanged( const QString &attribute, const QVariant &newValue )
810{
811 if ( mAttributeForm )
812 {
813 mAttributeForm->parentFormValueChanged( attribute, newValue );
814 }
815}
816
817void QgsDualView::hideEvent( QHideEvent *event )
818{
819 Q_UNUSED( event )
820 saveRecentDisplayExpressions();
821
822 // Better to save settings here than in destructor. This last can be called after a new
823 // project is loaded. So, when Qgis::ProjectFlag::RememberAttributeTableWindowsBetweenSessions is set,
824 // a new QgsDualView is created at project loading and we restore the old settings before saving the
825 // new one.
826 // And also, we override close event to just hide in QgsDockableWidgetHelper::eventFilter, that's why hideEvent
827 conditionalFormattingSplitterState->setValue( mConditionalSplitter->saveState() );
828 attributeEditorSplitterState->setValue( mAttributeEditorViewSplitter->saveState() );
829}
830
831void QgsDualView::viewWillShowContextMenu( QMenu *menu, const QModelIndex &masterIndex )
832{
833 if ( !menu )
834 {
835 return;
836 }
837
838 QAction *copyContentAction = menu->addAction( tr( "Copy Cell Content" ) );
839 menu->addAction( copyContentAction );
840 connect( copyContentAction, &QAction::triggered, this, [masterIndex, this] {
841 const QVariant var = mMasterModel->data( masterIndex, Qt::DisplayRole );
842 QApplication::clipboard()->setText( var.toString() );
843 } );
844
845 QgsVectorLayer *vl = mFilterModel->layer();
846 QgsMapCanvas *canvas = mFilterModel->mapCanvas();
847 if ( canvas && vl && vl->isSpatial() )
848 {
849 QAction *zoomToFeatureAction = menu->addAction( tr( "Zoom to Feature" ) );
850 connect( zoomToFeatureAction, &QAction::triggered, this, &QgsDualView::zoomToCurrentFeature );
851
852 QAction *panToFeatureAction = menu->addAction( tr( "Pan to Feature" ) );
853 connect( panToFeatureAction, &QAction::triggered, this, &QgsDualView::panToCurrentFeature );
854
855 QAction *flashFeatureAction = menu->addAction( tr( "Flash Feature" ) );
856 connect( flashFeatureAction, &QAction::triggered, this, &QgsDualView::flashCurrentFeature );
857 }
858
859 //add user-defined actions to context menu
860 const QList<QgsAction> actions = mLayer->actions()->actions( QStringLiteral( "Field" ) );
861 if ( !actions.isEmpty() )
862 {
863 QAction *a = menu->addAction( tr( "Run Layer Action" ) );
864 a->setEnabled( false );
865
866 for ( const QgsAction &action : actions )
867 {
868 if ( !action.runable() )
869 continue;
870
871 if ( vl && !vl->isEditable() && action.isEnabledOnlyWhenEditable() )
872 continue;
873
874 QgsAttributeTableAction *a = new QgsAttributeTableAction( action.name(), this, action.id(), masterIndex );
875 menu->addAction( action.name(), a, &QgsAttributeTableAction::execute );
876 }
877 }
878 const QModelIndex rowSourceIndex = mMasterModel->index( masterIndex.row(), 0 );
879 if ( !rowSourceIndex.isValid() )
880 {
881 return;
882 }
883
884 //add actions from QgsMapLayerActionRegistry to context menu
886 const QList<QgsMapLayerAction *> registeredActions = QgsGui::mapLayerActionRegistry()->mapLayerActions( mLayer, Qgis::MapLayerActionTarget::Layer | Qgis::MapLayerActionTarget::SingleFeature, context );
887 if ( !registeredActions.isEmpty() )
888 {
889 //add a separator between user defined and standard actions
890 menu->addSeparator();
891
892 for ( QgsMapLayerAction *action : registeredActions )
893 {
894 QgsAttributeTableMapLayerAction *a = new QgsAttributeTableMapLayerAction( action->text(), this, action, rowSourceIndex );
895 menu->addAction( action->text(), a, &QgsAttributeTableMapLayerAction::execute );
896 }
897 }
898
899 // entries for multiple features layer actions
900 // only show if the context menu is shown over a selected row
901 const QgsFeatureId currentFid = mMasterModel->rowToId( masterIndex.row() );
902 if ( mLayer->selectedFeatureCount() > 1 && mLayer->selectedFeatureIds().contains( currentFid ) )
903 {
904 const QList<QgsMapLayerAction *> registeredActions = QgsGui::mapLayerActionRegistry()->mapLayerActions( mLayer, Qgis::MapLayerActionTarget::MultipleFeatures, context );
905 if ( !registeredActions.isEmpty() )
906 {
907 menu->addSeparator();
908 QAction *action = menu->addAction( tr( "Actions on Selection (%1)" ).arg( mLayer->selectedFeatureCount() ) );
909 action->setEnabled( false );
910
912 for ( QgsMapLayerAction *action : registeredActions )
913 {
914 menu->addAction( action->text(), action, [this, action, context]() {
915 Q_NOWARN_DEPRECATED_PUSH
916 action->triggerForFeatures( mLayer, mLayer->selectedFeatures() );
917 Q_NOWARN_DEPRECATED_POP
918 action->triggerForFeatures( mLayer, mLayer->selectedFeatures(), context );
919 } );
920 }
921 }
922 }
923
924 menu->addSeparator();
925 QgsAttributeTableAction *a = new QgsAttributeTableAction( tr( "Open Form" ), this, QUuid(), rowSourceIndex );
926 menu->addAction( tr( "Open Form…" ), a, &QgsAttributeTableAction::featureForm );
927}
928
929
930void QgsDualView::widgetWillShowContextMenu( QgsActionMenu *menu, const QModelIndex &atIndex )
931{
932 emit showContextMenuExternally( menu, mFilterModel->rowToId( atIndex ) );
933}
934
935
936void QgsDualView::showViewHeaderMenu( QPoint point )
937{
938 const int col = mTableView->columnAt( point.x() );
939
940 delete mHorizontalHeaderMenu;
941 mHorizontalHeaderMenu = new QMenu( this );
942
943 QAction *hide = new QAction( tr( "&Hide Column" ), mHorizontalHeaderMenu );
944 connect( hide, &QAction::triggered, this, &QgsDualView::hideColumn );
945 hide->setData( col );
946 mHorizontalHeaderMenu->addAction( hide );
947 QAction *setWidth = new QAction( tr( "&Set Width…" ), mHorizontalHeaderMenu );
948 connect( setWidth, &QAction::triggered, this, &QgsDualView::resizeColumn );
949 setWidth->setData( col );
950 mHorizontalHeaderMenu->addAction( setWidth );
951
952 QAction *setWidthAllColumns = new QAction( tr( "&Set All Column Widths…" ), mHorizontalHeaderMenu );
953 connect( setWidthAllColumns, &QAction::triggered, this, &QgsDualView::resizeAllColumns );
954 setWidthAllColumns->setData( col );
955 mHorizontalHeaderMenu->addAction( setWidthAllColumns );
956
957 QAction *optimizeWidth = new QAction( tr( "&Autosize" ), mHorizontalHeaderMenu );
958 connect( optimizeWidth, &QAction::triggered, this, &QgsDualView::autosizeColumn );
959 optimizeWidth->setData( col );
960 mHorizontalHeaderMenu->addAction( optimizeWidth );
961
962 QAction *optimizeWidthAllColumns = new QAction( tr( "&Autosize All Columns" ), mHorizontalHeaderMenu );
963 connect( optimizeWidthAllColumns, &QAction::triggered, this, &QgsDualView::autosizeAllColumns );
964 mHorizontalHeaderMenu->addAction( optimizeWidthAllColumns );
965
966
967 mHorizontalHeaderMenu->addSeparator();
968 QAction *organize = new QAction( tr( "&Organize Columns…" ), mHorizontalHeaderMenu );
969 connect( organize, &QAction::triggered, this, &QgsDualView::organizeColumns );
970 mHorizontalHeaderMenu->addAction( organize );
971 QAction *sort = new QAction( tr( "&Sort…" ), mHorizontalHeaderMenu );
972 connect( sort, &QAction::triggered, this, [this]() { modifySort(); } );
973 mHorizontalHeaderMenu->addAction( sort );
974
975 mHorizontalHeaderMenu->popup( mTableView->horizontalHeader()->mapToGlobal( point ) );
976}
977
978void QgsDualView::organizeColumns()
979{
980 if ( !mLayer )
981 {
982 return;
983 }
984
985 QgsOrganizeTableColumnsDialog dialog( mLayer, attributeTableConfig(), this );
986 if ( dialog.exec() == QDialog::Accepted )
987 {
988 const QgsAttributeTableConfig config = dialog.config();
989 setAttributeTableConfig( config );
990 }
991}
992
993void QgsDualView::tableColumnResized( int column, int width )
994{
995 QgsAttributeTableConfig config = mConfig;
996 const int sourceCol = config.mapVisibleColumnToIndex( column );
997 if ( sourceCol >= 0 && config.columnWidth( sourceCol ) != width )
998 {
999 config.setColumnWidth( sourceCol, width );
1000 setAttributeTableConfig( config );
1001 }
1002}
1003
1004void QgsDualView::hideColumn()
1005{
1006 QAction *action = qobject_cast<QAction *>( sender() );
1007 const int col = action->data().toInt();
1008 QgsAttributeTableConfig config = mConfig;
1009 const int sourceCol = mConfig.mapVisibleColumnToIndex( col );
1010 if ( sourceCol >= 0 )
1011 {
1012 config.setColumnHidden( sourceCol, true );
1013 setAttributeTableConfig( config );
1014 }
1015}
1016
1017void QgsDualView::resizeColumn()
1018{
1019 QAction *action = qobject_cast<QAction *>( sender() );
1020 const int col = action->data().toInt();
1021 if ( col < 0 )
1022 return;
1023
1024 QgsAttributeTableConfig config = mConfig;
1025 const int sourceCol = config.mapVisibleColumnToIndex( col );
1026 if ( sourceCol >= 0 )
1027 {
1028 bool ok = false;
1029 const int width = QInputDialog::getInt( this, tr( "Set column width" ), tr( "Enter column width" ), mTableView->columnWidth( col ), 0, 1000, 10, &ok );
1030 if ( ok )
1031 {
1032 config.setColumnWidth( sourceCol, width );
1033 setAttributeTableConfig( config );
1034 }
1035 }
1036}
1037
1038void QgsDualView::resizeAllColumns()
1039{
1040 QAction *action = qobject_cast<QAction *>( sender() );
1041 const int col = action->data().toInt();
1042 if ( col < 0 )
1043 return;
1044
1045 QgsAttributeTableConfig config = mConfig;
1046
1047 bool ok = false;
1048 const int width = QInputDialog::getInt( this, tr( "Set Column Width" ), tr( "Enter column width" ), mTableView->columnWidth( col ), 1, 1000, 10, &ok );
1049 if ( ok )
1050 {
1051 const int colCount = mTableView->model()->columnCount();
1052 if ( colCount > 0 )
1053 {
1054 for ( int i = 0; i < colCount; i++ )
1055 {
1056 config.setColumnWidth( i, width );
1057 }
1058 setAttributeTableConfig( config );
1059 }
1060 }
1061}
1062
1063void QgsDualView::autosizeColumn()
1064{
1065 QAction *action = qobject_cast<QAction *>( sender() );
1066 const int col = action->data().toInt();
1067 mTableView->resizeColumnToContents( col );
1068}
1069
1070void QgsDualView::autosizeAllColumns()
1071{
1072 mTableView->resizeColumnsToContents();
1073}
1074
1075bool QgsDualView::modifySort()
1076{
1077 if ( !mLayer )
1078 return false;
1079
1080 QgsAttributeTableConfig config = mConfig;
1081
1082 QDialog orderByDlg;
1083 orderByDlg.setWindowTitle( tr( "Configure Attribute Table Sort Order" ) );
1084 QDialogButtonBox *dialogButtonBox = new QDialogButtonBox( QDialogButtonBox::Ok | QDialogButtonBox::Cancel );
1085 QGridLayout *layout = new QGridLayout();
1086 connect( dialogButtonBox, &QDialogButtonBox::accepted, &orderByDlg, &QDialog::accept );
1087 connect( dialogButtonBox, &QDialogButtonBox::rejected, &orderByDlg, &QDialog::reject );
1088 orderByDlg.setLayout( layout );
1089
1090 QGroupBox *sortingGroupBox = new QGroupBox();
1091 sortingGroupBox->setTitle( tr( "Defined sort order in attribute table" ) );
1092 sortingGroupBox->setCheckable( true );
1093 sortingGroupBox->setChecked( !sortExpression().isEmpty() );
1094 layout->addWidget( sortingGroupBox );
1095 sortingGroupBox->setLayout( new QGridLayout() );
1096
1097 QgsExpressionBuilderWidget *expressionBuilder = new QgsExpressionBuilderWidget();
1099
1100 expressionBuilder->initWithLayer( mLayer, context, QStringLiteral( "generic" ) );
1101 expressionBuilder->setExpressionText( sortExpression().isEmpty() ? mLayer->displayExpression() : sortExpression() );
1102
1103 sortingGroupBox->layout()->addWidget( expressionBuilder );
1104
1105 QCheckBox *cbxSortAscending = new QCheckBox( tr( "Sort ascending" ) );
1106 cbxSortAscending->setChecked( config.sortOrder() == Qt::AscendingOrder );
1107 sortingGroupBox->layout()->addWidget( cbxSortAscending );
1108
1109 layout->addWidget( dialogButtonBox );
1110 if ( orderByDlg.exec() )
1111 {
1112 const Qt::SortOrder sortOrder = cbxSortAscending->isChecked() ? Qt::AscendingOrder : Qt::DescendingOrder;
1113 if ( sortingGroupBox->isChecked() )
1114 {
1115 setSortExpression( expressionBuilder->expressionText(), sortOrder );
1116 config.setSortExpression( expressionBuilder->expressionText() );
1117 config.setSortOrder( sortOrder );
1118 }
1119 else
1120 {
1121 setSortExpression( QString(), sortOrder );
1122 config.setSortExpression( QString() );
1123 }
1124
1125 return true;
1126 }
1127 else
1128 {
1129 return false;
1130 }
1131}
1132
1134{
1135 QSet<int> attributes;
1136
1137 const QgsAttributeTableConfig config { layer->attributeTableConfig() };
1138
1139 const QVector<QgsAttributeTableConfig::ColumnConfig> constColumnconfigs { config.columns() };
1140 for ( const QgsAttributeTableConfig::ColumnConfig &columnConfig : std::as_const( constColumnconfigs ) )
1141 {
1142 if ( columnConfig.type == QgsAttributeTableConfig::Type::Field && !columnConfig.hidden )
1143 {
1144 attributes.insert( layer->fields().lookupField( columnConfig.name ) );
1145 }
1146 }
1147
1148 const QSet<int> colAttrs { attributes };
1149 for ( const int attrIdx : std::as_const( colAttrs ) )
1150 {
1151 if ( layer->fields().fieldOrigin( attrIdx ) == Qgis::FieldOrigin::Expression )
1152 {
1153 attributes += QgsExpression( layer->expressionField( attrIdx ) ).referencedAttributeIndexes( layer->fields() );
1154 }
1155 }
1156
1157 QgsAttributeList attrs { attributes.values() };
1158 std::sort( attrs.begin(), attrs.end() );
1159 return attrs;
1160}
1161
1162void QgsDualView::zoomToCurrentFeature()
1163{
1164 const QModelIndex currentIndex = mTableView->currentIndex();
1165 if ( !currentIndex.isValid() )
1166 {
1167 return;
1168 }
1169
1170 QgsFeatureIds ids;
1171 ids.insert( mFilterModel->rowToId( currentIndex ) );
1172 QgsMapCanvas *canvas = mFilterModel->mapCanvas();
1173 if ( canvas )
1174 {
1175 canvas->zoomToFeatureIds( mLayer, ids );
1176 }
1177}
1178
1179void QgsDualView::panToCurrentFeature()
1180{
1181 const QModelIndex currentIndex = mTableView->currentIndex();
1182 if ( !currentIndex.isValid() )
1183 {
1184 return;
1185 }
1186
1187 QgsFeatureIds ids;
1188 ids.insert( mFilterModel->rowToId( currentIndex ) );
1189 QgsMapCanvas *canvas = mFilterModel->mapCanvas();
1190 if ( canvas )
1191 {
1192 canvas->panToFeatureIds( mLayer, ids );
1193 }
1194}
1195
1196void QgsDualView::flashCurrentFeature()
1197{
1198 const QModelIndex currentIndex = mTableView->currentIndex();
1199 if ( !currentIndex.isValid() )
1200 {
1201 return;
1202 }
1203
1204 QgsFeatureIds ids;
1205 ids.insert( mFilterModel->rowToId( currentIndex ) );
1206 QgsMapCanvas *canvas = mFilterModel->mapCanvas();
1207 if ( canvas )
1208 {
1209 canvas->flashFeatureIds( mLayer, ids );
1210 }
1211}
1212
1213void QgsDualView::rebuildFullLayerCache()
1214{
1215 connect( mLayerCache, &QgsVectorLayerCache::progress, this, &QgsDualView::progress, Qt::UniqueConnection );
1216 connect( mLayerCache, &QgsVectorLayerCache::finished, this, &QgsDualView::finished, Qt::UniqueConnection );
1217
1218 mLayerCache->setFullCache( true );
1219}
1220
1221void QgsDualView::previewExpressionChanged( const QString &expression )
1222{
1223 mLayer->setDisplayExpression( expression );
1224}
1225
1226void QgsDualView::onSortColumnChanged()
1227{
1229 if ( cfg.sortExpression() != mFilterModel->sortExpression() || cfg.sortOrder() != mFilterModel->sortOrder() )
1230 {
1231 cfg.setSortExpression( mFilterModel->sortExpression() );
1232 cfg.setSortOrder( mFilterModel->sortOrder() );
1234 }
1235}
1236
1237void QgsDualView::updateSelectedFeatures()
1238{
1239 QgsFeatureRequest r = mMasterModel->request();
1241 return; // already requested all features
1242
1243 r.setFilterFids( masterModel()->layer()->selectedFeatureIds() );
1244 mMasterModel->setRequest( r );
1245 mMasterModel->loadLayer();
1246 emit filterChanged();
1247}
1248
1249void QgsDualView::updateEditedAddedFeatures()
1250{
1251 QgsFeatureRequest r = mMasterModel->request();
1253 return; // already requested all features
1254
1255 r.setFilterFids( masterModel()->layer()->editBuffer() ? masterModel()->layer()->editBuffer()->allAddedOrEditedFeatures() : QgsFeatureIds() );
1256 mMasterModel->setRequest( r );
1257 mMasterModel->loadLayer();
1258 emit filterChanged();
1259}
1260
1261void QgsDualView::extentChanged()
1262{
1263 QgsFeatureRequest r = mMasterModel->request();
1264 if ( mFilterModel->mapCanvas() && ( r.filterType() != Qgis::FeatureRequestFilterType::NoFilter || !r.filterRect().isNull() ) )
1265 {
1266 const QgsRectangle rect = mFilterModel->mapCanvas()->mapSettings().mapToLayerCoordinates( mLayer, mFilterModel->mapCanvas()->extent() );
1267 r.setFilterRect( rect );
1268 mMasterModel->setRequest( r );
1269 mMasterModel->loadLayer();
1270 }
1271 emit filterChanged();
1272}
1273
1274void QgsDualView::featureFormAttributeChanged( const QString &attribute, const QVariant &value, bool attributeChanged )
1275{
1276 Q_UNUSED( attribute )
1277 Q_UNUSED( value )
1278 if ( attributeChanged )
1279 {
1280 mFeatureListView->setCurrentFeatureEdited( true );
1281 mAttributeForm->save();
1282 }
1283}
1284
1286{
1287 mFilterModel->setFilteredFeatures( filteredFeatures );
1288}
1289
1290void QgsDualView::filterFeatures( const QgsExpression &filterExpression, const QgsExpressionContext &context )
1291{
1292 mFilterModel->setFilterExpression( filterExpression, context );
1293 mFilterModel->filterFeatures();
1294}
1295
1296
1298{
1299 mMasterModel->setRequest( request );
1300}
1301
1303{
1304 mTableView->setFeatureSelectionManager( featureSelectionManager );
1305 mFeatureListView->setFeatureSelectionManager( featureSelectionManager );
1306
1307 if ( mFeatureSelectionManager && mFeatureSelectionManager->parent() == this )
1308 delete mFeatureSelectionManager;
1309
1310 mFeatureSelectionManager = featureSelectionManager;
1311}
1312
1314{
1315 mConfig = config;
1316 mConfig.update( mLayer->fields() );
1317 mLayer->setAttributeTableConfig( mConfig );
1318 mFilterModel->setAttributeTableConfig( mConfig );
1319 mTableView->setAttributeTableConfig( mConfig );
1320 const QgsAttributeList attributes { requiredAttributes( mLayer ) };
1321 QgsFeatureRequest request = mMasterModel->request();
1322 // if the sort expression needs geometry reset the flag
1323 if ( QgsExpression( config.sortExpression() ).needsGeometry() )
1324 {
1325 mLayerCache->setCacheGeometry( true );
1326 request.setFlags( request.flags().setFlag( Qgis::FeatureRequestFlag::NoGeometry, false ) );
1327 }
1328 request.setSubsetOfAttributes( attributes );
1329 mMasterModel->setRequest( request );
1330 mLayerCache->setCacheSubsetOfAttributes( attributes );
1331}
1332
1333void QgsDualView::setSortExpression( const QString &sortExpression, Qt::SortOrder sortOrder )
1334{
1335 if ( sortExpression.isNull() )
1336 mFilterModel->sort( -1 );
1337 else
1338 mFilterModel->sort( sortExpression, sortOrder );
1339
1341 mConfig.setSortOrder( sortOrder );
1342 setAttributeTableConfig( mConfig );
1343}
1344
1346{
1347 return mFilterModel->sortExpression();
1348}
1349
1351{
1352 return mConfig;
1353}
1354
1355void QgsDualView::progress( int i, bool &cancel )
1356{
1357 if ( !mProgressDlg )
1358 {
1359 mProgressDlg = new QProgressDialog( tr( "Loading features…" ), tr( "Abort" ), 0, 0, this );
1360 mProgressDlg->setWindowTitle( tr( "Attribute Table" ) );
1361 mProgressDlg->setWindowModality( Qt::WindowModal );
1362 mProgressDlg->show();
1363 }
1364
1365 mProgressDlg->setLabelText( tr( "%L1 features loaded." ).arg( i ) );
1366 QCoreApplication::processEvents();
1367
1368 cancel = mProgressDlg && mProgressDlg->wasCanceled();
1369}
1370
1371void QgsDualView::finished()
1372{
1373 delete mProgressDlg;
1374 mProgressDlg = nullptr;
1375}
1376
1377/*
1378 * QgsAttributeTableAction
1379 */
1380
1382{
1383 mDualView->masterModel()->executeAction( mAction, mFieldIdx );
1384}
1385
1387{
1388 QgsFeatureIds editedIds;
1389 editedIds << mDualView->masterModel()->rowToId( mFieldIdx.row() );
1390 mDualView->setCurrentEditSelection( editedIds );
1392}
1393
1394/*
1395 * QgsAttributeTableMapLayerAction
1396 */
1397
1399{
1401 mDualView->masterModel()->executeMapLayerAction( mAction, mFieldIdx, context );
1402}
@ SelectAtId
Fast access to features using their ID.
@ 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.
@ 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()
Emitted when the 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 finished()
Emitted when the model has completely loaded all features.
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:95
static QgsShortcutsManager * shortcutsManager()
Returns the global shortcuts manager, used for managing a QAction and QShortcut sequences.
Definition qgsgui.cpp:125
static QgsMapLayerActionRegistry * mapLayerActionRegistry()
Returns the global map layer action registry, used for registering map layer actions.
Definition qgsgui.cpp:135
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.
A QScrollArea subclass with improved scrolling behavior.
static QgsSettingsTreeNode * sTreeWindowState
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.
static QVariant createNullVariant(QMetaType::Type metaType)
Helper method to properly create a null QVariant from a metaType Returns the created QVariant.
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:5970
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.