QGIS API Documentation 4.1.0-Master (3fcefe620d1)
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 "qgsdualview.h"
17
18#include "qgsactionmanager.h"
19#include "qgsactionmenu.h"
20#include "qgsapplication.h"
25#include "qgsfeaturelistmodel.h"
26#include "qgsfieldcalculator.h"
28#include "qgsgui.h"
30#include "qgsmapcanvas.h"
31#include "qgsmapcanvasutils.h"
32#include "qgsmaplayeraction.h"
33#include "qgsmessagebar.h"
35#include "qgsscrollarea.h"
36#include "qgssettings.h"
38#include "qgssettingstree.h"
39#include "qgsshortcutsmanager.h"
41#include "qgsvectorlayercache.h"
44
45#include <QClipboard>
46#include <QDialog>
47#include <QGroupBox>
48#include <QInputDialog>
49#include <QMenu>
50#include <QMessageBox>
51#include <QProgressDialog>
52#include <QShortcut>
53#include <QString>
54#include <QTimer>
55
56#include "moc_qgsdualview.cpp"
57
58using namespace Qt::StringLiterals;
59
61 = new QgsSettingsEntryBool( u"feature-list-highlight-feature"_s, QgsSettingsTree::sTreeAttributeTable, true, QObject::tr( "Whether to highlight/flash features in the feature list view" ) );
62
64 = new QgsSettingsEntryInteger( u"row-cache"_s, QgsSettingsTree::sTreeAttributeTable, 10000, QObject::tr( "Maximum number of rows to cache in the attribute table (0 means cache all rows)" ) );
65
66const std::unique_ptr<QgsSettingsEntryVariant> QgsDualView::conditionalFormattingSplitterState = std::make_unique<
67 QgsSettingsEntryVariant>( u"attribute-table-splitter-state"_s, QgsSettingsTree::sTreeWindowState, QgsVariantUtils::createNullVariant( QMetaType::Type::QByteArray ), u"State of conditional formatting splitter's layout so it could be restored when opening attribute table view."_s );
68const std::unique_ptr<QgsSettingsEntryVariant> QgsDualView::attributeEditorSplitterState = std::make_unique<
69 QgsSettingsEntryVariant>( u"attribute-editor-splitter-state"_s, QgsSettingsTree::sTreeWindowState, QgsVariantUtils::createNullVariant( QMetaType::Type::QByteArray ), u"State of attribute editor splitter's layout so it could be restored when opening attribute editor view."_s );
70
71QgsDualView::QgsDualView( QWidget *parent )
72 : QStackedWidget( parent )
73{
74 setupUi( this );
75 connect( mFeatureListView, &QgsFeatureListView::aboutToChangeEditSelection, this, &QgsDualView::featureListAboutToChangeEditSelection );
76 connect( mFeatureListView, &QgsFeatureListView::currentEditSelectionChanged, this, &QgsDualView::featureListCurrentEditSelectionChanged );
77 connect( mFeatureListView, &QgsFeatureListView::currentEditSelectionProgressChanged, this, &QgsDualView::updateEditSelectionProgress );
78 connect( mFeatureListView, &QgsFeatureListView::willShowContextMenu, this, &QgsDualView::widgetWillShowContextMenu );
79
80 connect( mTableView, &QgsAttributeTableView::willShowContextMenu, this, &QgsDualView::viewWillShowContextMenu );
81 mTableView->horizontalHeader()->setContextMenuPolicy( Qt::CustomContextMenu );
82 connect( mTableView->horizontalHeader(), &QHeaderView::customContextMenuRequested, this, &QgsDualView::showViewHeaderMenu );
83 connect( mTableView, &QgsAttributeTableView::columnResized, this, &QgsDualView::tableColumnResized );
84
85 mConditionalFormatWidgetStack->hide();
86 mConditionalFormatWidget = new QgsFieldConditionalFormatWidget( this );
87 mConditionalFormatWidgetStack->setMainPanel( mConditionalFormatWidget );
88 mConditionalFormatWidget->setDockMode( true );
89
90 const QgsSettings settings;
91 // copy old setting
92 conditionalFormattingSplitterState->copyValueFromKey( u"/qgis/attributeTable/splitterState"_s, true );
93 mConditionalSplitter->restoreState( conditionalFormattingSplitterState->value().toByteArray() );
94 mAttributeEditorViewSplitter->restoreState( attributeEditorSplitterState->value().toByteArray() );
95
96 mPreviewColumnsMenu = new QMenu( this );
97 mActionPreviewColumnsMenu->setMenu( mPreviewColumnsMenu );
98
99 // Set preview icon
100 mActionExpressionPreview->setIcon( QgsApplication::getThemeIcon( u"/mIconExpressionPreview.svg"_s ) );
101 mActionExpressionPreview->setText( tr( "Expression" ) );
102
103 // Connect layer list preview signals
104 connect( mActionExpressionPreview, &QAction::triggered, this, &QgsDualView::previewExpressionBuilder );
105 connect( mFeatureListView, &QgsFeatureListView::displayExpressionChanged, this, &QgsDualView::previewExpressionChanged );
106
107 // browsing toolbar
108 connect( mNextFeatureButton, &QToolButton::clicked, mFeatureListView, &QgsFeatureListView::editNextFeature );
109 connect( mPreviousFeatureButton, &QToolButton::clicked, mFeatureListView, &QgsFeatureListView::editPreviousFeature );
110 connect( mFirstFeatureButton, &QToolButton::clicked, mFeatureListView, &QgsFeatureListView::editFirstFeature );
111 connect( mLastFeatureButton, &QToolButton::clicked, mFeatureListView, &QgsFeatureListView::editLastFeature );
112
113 auto createShortcuts = [this]( const QString &objectName, void ( QgsFeatureListView::*slot )() ) {
114 QShortcut *sc = QgsGui::shortcutsManager()->shortcutByName( objectName );
115 // do not assert for sc as it would lead to crashes in testing
116 // or when using custom widgets lib if built with Debug
117 if ( sc )
118 connect( sc, &QShortcut::activated, mFeatureListView, slot );
119 };
120 createShortcuts( u"mAttributeTableFirstEditedFeature"_s, &QgsFeatureListView::editFirstFeature );
121 createShortcuts( u"mAttributeTablePreviousEditedFeature"_s, &QgsFeatureListView::editPreviousFeature );
122 createShortcuts( u"mAttributeTableNextEditedFeature"_s, &QgsFeatureListView::editNextFeature );
123 createShortcuts( u"mAttributeTableLastEditedFeature"_s, &QgsFeatureListView::editLastFeature );
124
125 QButtonGroup *buttonGroup = new QButtonGroup( this );
126 buttonGroup->setExclusive( false );
127 buttonGroup->addButton( mAutoPanButton, PanToFeature );
128 buttonGroup->addButton( mAutoZoomButton, ZoomToFeature );
129 const FeatureListBrowsingAction action = QgsSettings().enumValue( u"/qgis/attributeTable/featureListBrowsingAction"_s, NoAction );
130 QAbstractButton *bt = buttonGroup->button( static_cast<int>( action ) );
131 if ( bt )
132 bt->setChecked( true );
133 connect( buttonGroup, qOverload<QAbstractButton *, bool>( &QButtonGroup::buttonToggled ), this, &QgsDualView::panZoomGroupButtonToggled );
134 mFlashButton->setChecked( settingsFeatureListHighlightFeature->value() );
135 connect( mFlashButton, &QToolButton::clicked, this, &QgsDualView::flashButtonClicked );
136}
137
140
141void QgsDualView::init( QgsVectorLayer *layer, QgsMapCanvas *mapCanvas, const QgsFeatureRequest &request, const QgsAttributeEditorContext &context, bool loadFeatures, bool showFirstFeature )
142{
143 if ( !layer )
144 return;
145
146 delete mAttributeForm;
147 mAttributeForm = nullptr;
148
149 mLayer = layer;
150
151 // Keep fields order in sync: force config reset
152 connect( mLayer, &QgsVectorLayer::updatedFields, this, [this] {
153 // Prevent crash if a field was deleted when the sort expression contained a reference to it.
154 mConfig.update( mLayer->fields() );
155 mFilterModel->setAttributeTableConfig( attributeTableConfig(), /* force */ true );
156 } );
157
158 mEditorContext = context;
159
160 // create an empty form to find out if it needs geometry or not
161 const QgsAttributeForm emptyForm( mLayer, QgsFeature(), mEditorContext );
162
163 const QgsExpression sortingExpression = QgsExpression( mConfig.sortExpression() );
164
165 const bool needsGeometry = mLayer->conditionalStyles()->rulesNeedGeometry()
168 || emptyForm.needsGeometry()
169 || sortingExpression.needsGeometry();
170
171 initLayerCache( needsGeometry );
172 initModels( mapCanvas, request, loadFeatures );
173
174 mConditionalFormatWidget->setLayer( mLayer );
175
176 mTableView->setModel( mFilterModel );
177 mFeatureListView->setModel( mFeatureListModel );
178
179 connect( mFilterModel, &QgsAttributeTableFilterModel::sortColumnChanged, this, &QgsDualView::onSortColumnChanged );
180 connect( mLayer, &QgsVectorLayer::afterCommitChanges, this, [this] { mFeatureListView->setCurrentFeatureEdited( false ); } );
181
182 if ( mFeatureListPreviewButton->defaultAction() )
183 {
184 mFeatureListView->setDisplayExpression( mDisplayExpression );
185 }
186 else
187 {
188 columnBoxInit();
189 }
190
191 // This slows down load of the attribute table heaps and uses loads of memory.
192 //mTableView->resizeColumnsToContents();
193
194 if ( showFirstFeature && mFeatureListModel->rowCount() > 0 )
195 {
196 mFeatureListView->setEditSelection( QgsFeatureIds() << mFeatureListModel->data( mFeatureListModel->index( 0, 0 ), QgsFeatureListModel::Role::FeatureRole ).value<QgsFeature>().id() );
197 }
198 else
199 {
200 // Set attribute table config to allow for proper sorting ordering in feature list view
201 setAttributeTableConfig( mLayer->attributeTableConfig() );
202 }
203}
204
205void QgsDualView::initAttributeForm( const QgsFeature &feature )
206{
207 Q_ASSERT( !mAttributeForm );
208
209 mAttributeForm = new QgsAttributeForm( mLayer, feature, mEditorContext );
210 if ( !mEditorContext.parentContext() )
211 {
212 mAttributeEditorScrollArea = new QgsScrollArea();
213 mAttributeEditorScrollArea->setWidgetResizable( true );
214 mAttributeEditor->layout()->addWidget( mAttributeEditorScrollArea );
215 mAttributeEditorScrollArea->setWidget( mAttributeForm );
216 }
217 else
218 {
219 mAttributeEditor->layout()->addWidget( mAttributeForm );
220 }
221
222 // This is an arbitrary yet small value to fix issue GH #50181
223 // the default value is 0.
224 mAttributeForm->setMinimumWidth( 200 );
225
226 setAttributeTableConfig( mLayer->attributeTableConfig() );
227
228 connect( mAttributeForm, &QgsAttributeForm::widgetValueChanged, this, &QgsDualView::featureFormAttributeChanged );
229 connect( mAttributeForm, &QgsAttributeForm::modeChanged, this, &QgsDualView::formModeChanged );
231 connect( mAttributeForm, &QgsAttributeForm::flashFeatures, this, [this]( const QString &filter ) {
232 if ( QgsMapCanvas *canvas = mFilterModel->mapCanvas() )
233 {
234 QgsMapCanvasUtils::flashMatchingFeatures( canvas, mLayer, filter );
235 }
236 } );
237 connect( mAttributeForm, &QgsAttributeForm::zoomToFeatures, this, [this]( const QString &filter ) {
238 if ( QgsMapCanvas *canvas = mFilterModel->mapCanvas() )
239 {
240 QgsMapCanvasUtils::zoomToMatchingFeatures( canvas, mLayer, filter );
241 }
242 } );
243
244 connect( mMasterModel, &QgsAttributeTableModel::modelChanged, mAttributeForm, &QgsAttributeForm::refreshFeature );
245}
246
247void QgsDualView::columnBoxInit()
248{
249 // load fields
250 const QList<QgsField> fields = mLayer->fields().toList();
251 const QString displayExpression = mLayer->displayExpression();
252
253 mFeatureListPreviewButton->addAction( mActionExpressionPreview );
254 mFeatureListPreviewButton->addAction( mActionPreviewColumnsMenu );
255
256 QAction *defaultFieldAction = nullptr;
257 const auto constFields = fields;
258 for ( const QgsField &field : constFields )
259 {
260 const int fieldIndex = mLayer->fields().lookupField( field.name() );
261 if ( fieldIndex == -1 )
262 continue;
263
264 const QString fieldName = field.name();
265 if ( QgsGui::editorWidgetRegistry()->findBest( mLayer, fieldName ).type() != "Hidden"_L1 )
266 {
267 const QIcon fieldIcon = mLayer->fields().iconForField( fieldIndex );
268 const QString fieldDisplayName = mLayer->attributeDisplayName( fieldIndex );
269
270 // Generate action for the preview popup button of the feature list
271 QAction *previewAction = new QAction( fieldIcon, fieldDisplayName, mFeatureListPreviewButton );
272 connect( previewAction, &QAction::triggered, this, [this, previewAction, fieldName] { previewColumnChanged( previewAction, fieldName ); } );
273 mPreviewColumnsMenu->addAction( previewAction );
274
275 if ( fieldDisplayName == displayExpression
276 || u"COALESCE( \"%1\", '<NULL>' )"_s.arg( fieldDisplayName ) == displayExpression
277 || u"\"%1\""_s.arg( fieldDisplayName ) == displayExpression
278 || fieldName == displayExpression
279 || u"COALESCE( \"%1\", '<NULL>' )"_s.arg( fieldName ) == displayExpression
280 || u"\"%1\""_s.arg( fieldName ) == displayExpression )
281 {
282 defaultFieldAction = previewAction;
283 }
284 }
285 }
286
287 QMenu *sortMenu = new QMenu( this );
288 QAction *sortMenuAction = new QAction( QgsApplication::getThemeIcon( u"sort.svg"_s ), tr( "Sort…" ), this );
289 sortMenuAction->setMenu( sortMenu );
290
291 QAction *sortByPreviewExpressionAsc = new QAction( QgsApplication::getThemeIcon( u"sort.svg"_s ), tr( "By Display Name (Ascending)" ), this );
292 connect( sortByPreviewExpressionAsc, &QAction::triggered, this, [this]() { mFeatureListModel->setSortByDisplayExpression( true, Qt::AscendingOrder ); } );
293 sortMenu->addAction( sortByPreviewExpressionAsc );
294 QAction *sortByPreviewExpressionDesc = new QAction( QgsApplication::getThemeIcon( u"sort-reverse.svg"_s ), tr( "By Display Name (Descending)" ), this );
295 connect( sortByPreviewExpressionDesc, &QAction::triggered, this, [this]() { mFeatureListModel->setSortByDisplayExpression( true, Qt::DescendingOrder ); } );
296 sortMenu->addAction( sortByPreviewExpressionDesc );
297 QAction *sortByPreviewExpressionCustom = new QAction( QgsApplication::getThemeIcon( u"mIconExpressionPreview.svg"_s ), tr( "By Custom Expression" ), this );
298 connect( sortByPreviewExpressionCustom, &QAction::triggered, this, [this]() {
299 if ( modifySort() )
300 mFeatureListModel->setSortByDisplayExpression( false );
301 } );
302 sortMenu->addAction( sortByPreviewExpressionCustom );
303
304 mFeatureListPreviewButton->addAction( sortMenuAction );
305
306 QAction *separator = new QAction( mFeatureListPreviewButton );
307 separator->setSeparator( true );
308 mFeatureListPreviewButton->addAction( separator );
309 restoreRecentDisplayExpressions();
310
311 if ( defaultFieldAction )
312 {
313 mFeatureListPreviewButton->setDefaultAction( defaultFieldAction );
314 mFeatureListPreviewButton->defaultAction()->trigger();
315 }
316 else
317 {
318 mActionExpressionPreview->setToolTip( displayExpression );
319 mFeatureListPreviewButton->setDefaultAction( mActionExpressionPreview );
320
321 mFeatureListView->setDisplayExpression( displayExpression );
322 setDisplayExpression( displayExpression.isEmpty() ? tr( "'[Please define preview text]'" ) : displayExpression );
323 }
324}
325
327{
328 setCurrentIndex( view );
329}
330
332{
333 return static_cast<QgsDualView::ViewMode>( currentIndex() );
334}
335
337{
338 // cleanup any existing connections
339 switch ( mFilterModel->filterMode() )
340 {
342 disconnect( mFilterModel->mapCanvas(), &QgsMapCanvas::extentsChanged, this, &QgsDualView::extentChanged );
344 break;
345
349 disconnect( mFilterModel, &QgsAttributeTableFilterModel::filterError, this, &QgsDualView::filterError );
350 break;
351
354 disconnect( mFilterModel, &QgsAttributeTableFilterModel::filterError, this, &QgsDualView::filterError );
355 disconnect( masterModel()->layer(), &QgsVectorLayer::layerModified, this, &QgsDualView::updateEditedAddedFeatures );
356 break;
357
359 disconnect( masterModel()->layer(), &QgsVectorLayer::selectionChanged, this, &QgsDualView::updateSelectedFeatures );
360 break;
361
363 mMasterModel->setShowValidityState( false );
364 break;
365 }
366
367 QgsFeatureRequest request = mMasterModel->request();
368
369 // create an empty form to find out if it needs geometry or not
370 const QgsAttributeForm emptyForm( mLayer, QgsFeature(), mEditorContext );
371 const bool needsGeometry = ( filterMode == QgsAttributeTableFilterModel::ShowVisible ) || emptyForm.needsGeometry() || QgsExpression( mConfig.sortExpression() ).needsGeometry();
372
373 const bool requiresTableReload = ( request.filterType() != Qgis::Qgis::FeatureRequestFilterType::NoFilter
374 || request.spatialFilterType() != Qgis::SpatialFilterType::NoFilter ) // previous request was subset
375 || ( needsGeometry && request.flags() & Qgis::FeatureRequestFlag::NoGeometry ) // no geometry for last request
376 || ( mMasterModel->rowCount() == 0 ); // no features
377
378 request.setFlags( request.flags().setFlag( Qgis::FeatureRequestFlag::NoGeometry, !needsGeometry ) );
379 request.setFilterFids( QgsFeatureIds() );
380 request.setFilterRect( QgsRectangle() );
381 request.disableFilter();
382
383 // setup new connections and filter request parameters
384 switch ( filterMode )
385 {
387 connect( mFilterModel->mapCanvas(), &QgsMapCanvas::extentsChanged, this, &QgsDualView::extentChanged );
388 if ( mFilterModel->mapCanvas() )
389 {
390 const QgsRectangle rect = mFilterModel->mapCanvas()->mapSettings().mapToLayerCoordinates( mLayer, mFilterModel->mapCanvas()->extent() );
391 request.setFilterRect( rect );
392 }
394 break;
395
397 request.setFilterFids( masterModel()->layer()->editBuffer() ? masterModel()->layer()->editBuffer()->allAddedOrEditedFeatures() : QgsFeatureIds() );
399 connect( mFilterModel, &QgsAttributeTableFilterModel::filterError, this, &QgsDualView::filterError );
400 connect( masterModel()->layer(), &QgsVectorLayer::layerModified, this, &QgsDualView::updateEditedAddedFeatures );
401 break;
402
404 {
405 mMasterModel->setShowValidityState( true );
407 filterFeatures( u"is_feature_valid() = false"_s, context );
409 connect( mFilterModel, &QgsAttributeTableFilterModel::filterError, this, &QgsDualView::filterError );
410 break;
411 }
412
415 {
416 const QString filterExpression = filterMode == QgsAttributeTableFilterModel::ShowFilteredList ? mFilterModel->filterExpression() : QString();
417 if ( !filterExpression.isEmpty() )
418 request.setFilterExpression( mFilterModel->filterExpression() );
420 connect( mFilterModel, &QgsAttributeTableFilterModel::filterError, this, &QgsDualView::filterError );
421 break;
422 }
423
425 connect( masterModel()->layer(), &QgsVectorLayer::selectionChanged, this, &QgsDualView::updateSelectedFeatures );
426 request.setFilterFids( masterModel()->layer()->selectedFeatureIds() );
427 break;
428 }
429
430 // disable the browsing auto pan/scale if the list only shows visible items
431 switch ( filterMode )
432 {
434 setBrowsingAutoPanScaleAllowed( false );
435 break;
436
442 setBrowsingAutoPanScaleAllowed( true );
443 break;
444 }
445
446 if ( requiresTableReload )
447 {
448 //disconnect the connections of the current (old) filtermode before reload
449 mFilterModel->disconnectFilterModeConnections();
450
451 mMasterModel->setRequest( request );
452 whileBlocking( mLayerCache )->setCacheGeometry( needsGeometry );
453 mMasterModel->loadLayer();
454 }
455
456 //update filter model
457 mFilterModel->setFilterMode( filterMode );
458 emit filterChanged();
459}
460
461void QgsDualView::setSelectedOnTop( bool selectedOnTop )
462{
463 mFilterModel->setSelectedOnTop( selectedOnTop );
464}
465
466void QgsDualView::initLayerCache( bool cacheGeometry )
467{
468 // Initialize the cache
469 const int cacheSize = settingsAttributeTableRowCache->value();
470 mLayerCache = new QgsVectorLayerCache( mLayer, cacheSize, this );
471 mLayerCache->setCacheSubsetOfAttributes( requiredAttributes( mLayer ) );
472 mLayerCache->setCacheGeometry( cacheGeometry );
473 if ( 0 == cacheSize || !mLayer->dataProvider()->capabilities().testFlag( Qgis::VectorProviderCapability::SelectAtId ) )
474 {
475 connect( mLayerCache, &QgsVectorLayerCache::invalidated, this, &QgsDualView::rebuildFullLayerCache );
476 rebuildFullLayerCache();
477 }
478}
479
480void QgsDualView::initModels( QgsMapCanvas *mapCanvas, const QgsFeatureRequest &request, bool loadFeatures )
481{
482 delete mFeatureListModel;
483 delete mFilterModel;
484 delete mMasterModel;
485
486 mMasterModel = new QgsAttributeTableModel( mLayerCache, this );
487 mMasterModel->setRequest( request );
488 mMasterModel->setEditorContext( mEditorContext );
489 mMasterModel->setExtraColumns( 1 ); // Add one extra column which we can "abuse" as an action column
490
491 connect( mMasterModel, &QgsAttributeTableModel::progress, this, &QgsDualView::progress );
492 connect( mMasterModel, &QgsAttributeTableModel::finished, this, &QgsDualView::finished );
493
495
496 if ( loadFeatures )
497 mMasterModel->loadLayer();
498
499 mFilterModel = new QgsAttributeTableFilterModel( mapCanvas, mMasterModel, mMasterModel );
500
501 // The following connections to invalidate() are necessary to keep the filter model in sync
502 // see regression https://github.com/qgis/QGIS/issues/23890
503 connect( mMasterModel, &QgsAttributeTableModel::rowsRemoved, mFilterModel, &QgsAttributeTableFilterModel::invalidate );
504 connect( mMasterModel, &QgsAttributeTableModel::rowsInserted, mFilterModel, &QgsAttributeTableFilterModel::invalidate );
505
507
508 mFeatureListModel = new QgsFeatureListModel( mFilterModel, mFilterModel );
509}
510
511void QgsDualView::restoreRecentDisplayExpressions()
512{
513 const QVariantList previewExpressions = mLayer->customProperty( u"dualview/previewExpressions"_s ).toList();
514
515 for ( const QVariant &previewExpression : previewExpressions )
516 {
517 insertRecentlyUsedDisplayExpression( previewExpression.toString() );
518 }
519}
520
521void QgsDualView::saveRecentDisplayExpressions() const
522{
523 if ( !mLayer )
524 {
525 return;
526 }
527 const QList<QAction *> actions = mFeatureListPreviewButton->actions();
528
529 // Remove existing same action
530 int index = actions.indexOf( mLastDisplayExpressionAction );
531 if ( index != -1 )
532 {
533 QVariantList previewExpressions;
534 for ( ; index < actions.length(); ++index )
535 {
536 QAction *action = actions.at( index );
537 previewExpressions << action->property( "previewExpression" );
538 }
539
540 mLayer->setCustomProperty( u"dualview/previewExpressions"_s, previewExpressions );
541 }
542}
543
544void QgsDualView::setDisplayExpression( const QString &expression )
545{
546 mDisplayExpression = expression;
547 insertRecentlyUsedDisplayExpression( expression );
548}
549
550void QgsDualView::insertRecentlyUsedDisplayExpression( const QString &expression )
551{
552 const QList<QAction *> actions = mFeatureListPreviewButton->actions();
553
554 // Remove existing same action
555 const int index = actions.indexOf( mLastDisplayExpressionAction );
556 if ( index != -1 )
557 {
558 for ( int i = 0; index + i < actions.length(); ++i )
559 {
560 QAction *action = actions.at( index );
561 if ( action->text() == expression || i >= 9 )
562 {
563 if ( action == mLastDisplayExpressionAction )
564 {
565 mLastDisplayExpressionAction = nullptr;
566 }
567 mFeatureListPreviewButton->removeAction( action );
568 }
569 else
570 {
571 if ( !mLastDisplayExpressionAction )
572 {
573 mLastDisplayExpressionAction = action;
574 }
575 }
576 }
577 }
578
579 QString name = expression;
580 QIcon icon = QgsApplication::getThemeIcon( u"/mIconExpressionPreview.svg"_s );
581 if ( expression.startsWith( "COALESCE( \""_L1 ) && expression.endsWith( ", '<NULL>' )"_L1 ) )
582 {
583 name = expression.mid( 11, expression.length() - 24 ); // Numbers calculated from the COALESCE / <NULL> parts
584
585 const int fieldIndex = mLayer->fields().indexOf( name );
586 if ( fieldIndex != -1 )
587 {
588 name = mLayer->attributeDisplayName( fieldIndex );
589 icon = mLayer->fields().iconForField( fieldIndex );
590 }
591 else
592 {
593 name = expression;
594 }
595 }
596
597 QAction *previewAction = new QAction( icon, name, mFeatureListPreviewButton );
598 previewAction->setProperty( "previewExpression", expression );
599 connect( previewAction, &QAction::triggered, this, [expression, this]( bool ) {
600 setDisplayExpression( expression );
601 mFeatureListPreviewButton->setText( tr( "Expression" ) );
602 mFeatureListPreviewButton->setToolTip( expression );
603 } );
604
605 mFeatureListPreviewButton->insertAction( mLastDisplayExpressionAction, previewAction );
606 mLastDisplayExpressionAction = previewAction;
607}
608
609void QgsDualView::updateEditSelectionProgress( int progress, int count )
610{
611 mProgressCount->setText( u"%1 / %2"_s.arg( progress + 1 ).arg( count ) );
612 mPreviousFeatureButton->setEnabled( progress > 0 );
613 mNextFeatureButton->setEnabled( progress + 1 < count );
614 mFirstFeatureButton->setEnabled( progress > 0 );
615 mLastFeatureButton->setEnabled( progress + 1 < count );
616 if ( mAttributeForm )
617 {
618 mAttributeForm->setVisible( count > 0 );
619 }
620}
621
622void QgsDualView::panOrZoomToFeature( const QgsFeatureIds &featureset )
623{
624 QgsMapCanvas *canvas = mFilterModel->mapCanvas();
625 if ( canvas && view() == AttributeEditor && featureset != mLastFeatureSet )
626 {
627 if ( mBrowsingAutoPanScaleAllowed )
628 {
629 if ( mAutoPanButton->isChecked() )
630 QTimer::singleShot( 0, this, [this, featureset, canvas]() { canvas->panToFeatureIds( mLayer, featureset, false ); } );
631 else if ( mAutoZoomButton->isChecked() )
632 QTimer::singleShot( 0, this, [this, featureset, canvas]() { canvas->zoomToFeatureIds( mLayer, featureset ); } );
633 }
634 if ( mFlashButton->isChecked() )
635 QTimer::singleShot( 0, this, [this, featureset, canvas]() { canvas->flashFeatureIds( mLayer, featureset ); } );
636 mLastFeatureSet = featureset;
637 }
638}
639
640void QgsDualView::setBrowsingAutoPanScaleAllowed( bool allowed )
641{
642 if ( mBrowsingAutoPanScaleAllowed == allowed )
643 return;
644
645 mBrowsingAutoPanScaleAllowed = allowed;
646
647 mAutoPanButton->setEnabled( allowed );
648 mAutoZoomButton->setEnabled( allowed );
649
650 const QString disabledHint = tr( "(disabled when attribute table only shows features visible in the current map canvas extent)" );
651
652 mAutoPanButton->setToolTip( tr( "Automatically pan to the current feature" ) + ( allowed ? QString() : QString( ' ' ) + disabledHint ) );
653 mAutoZoomButton->setToolTip( tr( "Automatically zoom to the current feature" ) + ( allowed ? QString() : QString( ' ' ) + disabledHint ) );
654}
655
656void QgsDualView::panZoomGroupButtonToggled( QAbstractButton *button, bool checked )
657{
658 if ( button == mAutoPanButton && checked )
659 {
660 QgsSettings().setEnumValue( u"/qgis/attributeTable/featureListBrowsingAction"_s, PanToFeature );
661 mAutoZoomButton->setChecked( false );
662 }
663 else if ( button == mAutoZoomButton && checked )
664 {
665 QgsSettings().setEnumValue( u"/qgis/attributeTable/featureListBrowsingAction"_s, ZoomToFeature );
666 mAutoPanButton->setChecked( false );
667 }
668 else
669 {
670 QgsSettings().setEnumValue( u"/qgis/attributeTable/featureListBrowsingAction"_s, NoAction );
671 }
672
673 if ( checked && mLayer->isSpatial() )
674 panOrZoomToFeature( mFeatureListView->currentEditSelection() );
675}
676
677void QgsDualView::flashButtonClicked( bool clicked )
678{
679 settingsFeatureListHighlightFeature->setValue( clicked );
680 if ( !clicked )
681 return;
682
683 QgsMapCanvas *canvas = mFilterModel->mapCanvas();
684
685 if ( canvas )
686 canvas->flashFeatureIds( mLayer, mFeatureListView->currentEditSelection() );
687}
688
689void QgsDualView::filterError( const QString &errorMessage )
690{
691 if ( mEditorContext.mainMessageBar() )
692 {
693 mEditorContext.mainMessageBar()->pushWarning( tr( "An error occurred while filtering features" ), errorMessage );
694 }
695}
696
697void QgsDualView::featureListAboutToChangeEditSelection( bool &ok )
698{
699 if ( !mAttributeForm )
700 return;
701
702 if ( mLayer->isEditable() && !mAttributeForm->save() )
703 ok = false;
704}
705
706void QgsDualView::featureListCurrentEditSelectionChanged( const QgsFeature &feat )
707{
708 if ( !mAttributeForm )
709 {
710 initAttributeForm( feat );
711 }
712 else if ( !mLayer->isEditable() || mAttributeForm->save() )
713 {
714 mAttributeForm->setFeature( feat );
715 QgsFeatureIds featureset;
716 featureset << feat.id();
717 setCurrentEditSelection( featureset );
718
719 if ( mLayer->isSpatial() )
720 panOrZoomToFeature( featureset );
721 }
722 else
723 {
724 // Couldn't save feature
725 }
726}
727
729{
730 mFeatureListView->setCurrentFeatureEdited( false );
731 mFeatureListView->setEditSelection( fids );
732}
733
735{
736 return mAttributeForm ? mAttributeForm->save() : false;
737}
738
740{
741 mConditionalFormatWidgetStack->setVisible( !mConditionalFormatWidgetStack->isVisible() );
742}
743
745{
746 if ( !mAttributeForm )
747 return;
748
749 if ( enabled )
750 {
751 mPreviousView = view();
753 }
754 else
755 {
756 setView( mPreviousView );
757 }
758
760}
761
763{
764 if ( !mAttributeForm )
765 return;
766
767 if ( enabled )
768 {
770 mAttributeForm->setMode( QgsAttributeEditorContext::SearchMode );
771 mAttributeForm->setVisible( true );
772 }
773 else
774 {
775 mAttributeForm->setMode( QgsAttributeEditorContext::SingleEditMode );
776 mAttributeForm->setVisible( mFilterModel->rowCount() > 0 );
777 }
778}
779
780void QgsDualView::previewExpressionBuilder()
781{
782 // Show expression builder
784
785 QgsExpressionBuilderDialog dlg( mLayer, mFeatureListView->displayExpression(), this, u"generic"_s, context );
786 dlg.setWindowTitle( tr( "Expression Based Preview" ) );
787 dlg.setExpressionText( mFeatureListView->displayExpression() );
788
789 if ( dlg.exec() == QDialog::Accepted )
790 {
791 mFeatureListView->setDisplayExpression( dlg.expressionText() );
792 mActionExpressionPreview->setToolTip( dlg.expressionText() );
793
794 mFeatureListPreviewButton->setDefaultAction( mActionExpressionPreview );
795 mFeatureListPreviewButton->setPopupMode( QToolButton::MenuButtonPopup );
796 }
797
798 setDisplayExpression( mFeatureListView->displayExpression() );
799}
800
801void QgsDualView::previewColumnChanged( QAction *previewAction, const QString &expression )
802{
803 if ( !mFeatureListView->setDisplayExpression( u"COALESCE( \"%1\", '<NULL>' )"_s.arg( expression ) ) )
804 {
805 QMessageBox::warning( this, tr( "Column Display Name" ), tr( "Could not set column '%1' as display name.\nParser error:\n%2" ).arg( previewAction->text(), mFeatureListView->parserErrorString() ) );
806 }
807 else
808 {
809 mFeatureListPreviewButton->setText( previewAction->text() );
810 mFeatureListPreviewButton->setIcon( previewAction->icon() );
811 mFeatureListPreviewButton->setPopupMode( QToolButton::InstantPopup );
812 }
813
814 setDisplayExpression( mFeatureListView->displayExpression() );
815}
816
818{
819 return mMasterModel->rowCount();
820}
821
823{
824 return mFilterModel->rowCount();
825}
826
828{
829 const QModelIndex currentIndex = mTableView->currentIndex();
830 if ( !currentIndex.isValid() )
831 {
832 return;
833 }
834
835 const QVariant var = mMasterModel->data( currentIndex, Qt::DisplayRole );
836 QApplication::clipboard()->setText( var.toString() );
837}
838
840{
841 if ( mProgressDlg )
842 mProgressDlg->cancel();
843}
844
845void QgsDualView::parentFormValueChanged( const QString &attribute, const QVariant &newValue )
846{
847 if ( mAttributeForm )
848 {
849 mAttributeForm->parentFormValueChanged( attribute, newValue );
850 }
851}
852
853void QgsDualView::hideEvent( QHideEvent *event )
854{
855 Q_UNUSED( event )
856 saveRecentDisplayExpressions();
857
858 // Better to save settings here than in destructor. This last can be called after a new
859 // project is loaded. So, when Qgis::ProjectFlag::RememberAttributeTableWindowsBetweenSessions is set,
860 // a new QgsDualView is created at project loading and we restore the old settings before saving the
861 // new one.
862 // And also, we override close event to just hide in QgsDockableWidgetHelper::eventFilter, that's why hideEvent
863 conditionalFormattingSplitterState->setValue( mConditionalSplitter->saveState() );
864 attributeEditorSplitterState->setValue( mAttributeEditorViewSplitter->saveState() );
865}
866
867void QgsDualView::viewWillShowContextMenu( QMenu *menu, const QModelIndex &masterIndex )
868{
869 if ( !menu )
870 {
871 return;
872 }
873
874 const QVariant displayValue = mMasterModel->data( masterIndex, Qt::DisplayRole );
875
876 if ( displayValue.isValid() )
877 {
878 // get values for preview in menu
879 QString previewDisplayValue = displayValue.toString();
880 if ( previewDisplayValue.length() > 12 )
881 {
882 previewDisplayValue.truncate( 12 );
883 previewDisplayValue.append( u"…"_s );
884 }
885
886
887 QAction *copyContentAction = menu->addAction( tr( "Copy Cell Content (%1)" ).arg( previewDisplayValue ) );
888 menu->addAction( copyContentAction );
889 connect( copyContentAction, &QAction::triggered, this, [displayValue] { QApplication::clipboard()->setText( displayValue.toString() ); } );
890 }
891
892 const QVariant rawValue = mMasterModel->data( masterIndex, Qt::EditRole );
893 if ( rawValue.isValid() )
894 {
895 // get values for preview in menu
896 QString previewRawValue = rawValue.toString();
897 if ( previewRawValue.length() > 12 )
898 {
899 previewRawValue.truncate( 12 );
900 previewRawValue.append( u"…"_s );
901 }
902
903 QAction *copyRawContentAction = menu->addAction( tr( "Copy Raw Value (%1)" ).arg( previewRawValue ) );
904 menu->addAction( copyRawContentAction );
905 connect( copyRawContentAction, &QAction::triggered, this, [rawValue] { QApplication::clipboard()->setText( rawValue.toString() ); } );
906 }
907
908 QgsVectorLayer *vl = mFilterModel->layer();
909 QgsMapCanvas *canvas = mFilterModel->mapCanvas();
910 if ( canvas && vl && vl->isSpatial() )
911 {
912 QAction *zoomToFeatureAction = menu->addAction( tr( "Zoom to Feature" ) );
913 connect( zoomToFeatureAction, &QAction::triggered, this, &QgsDualView::zoomToCurrentFeature );
914
915 QAction *panToFeatureAction = menu->addAction( tr( "Pan to Feature" ) );
916 connect( panToFeatureAction, &QAction::triggered, this, &QgsDualView::panToCurrentFeature );
917
918 QAction *flashFeatureAction = menu->addAction( tr( "Flash Feature" ) );
919 connect( flashFeatureAction, &QAction::triggered, this, &QgsDualView::flashCurrentFeature );
920 }
921
922 //add user-defined actions to context menu
923 const QList<QgsAction> actions = mLayer->actions()->actions( u"Field"_s );
924 if ( !actions.isEmpty() )
925 {
926 QAction *a = menu->addAction( tr( "Run Layer Action" ) );
927 a->setEnabled( false );
928
929 for ( const QgsAction &action : actions )
930 {
931 if ( !action.runable() )
932 continue;
933
934 if ( vl && !vl->isEditable() && action.isEnabledOnlyWhenEditable() )
935 continue;
936
937 QgsAttributeTableAction *a = new QgsAttributeTableAction( action.name(), this, action.id(), masterIndex );
938 menu->addAction( action.name(), a, &QgsAttributeTableAction::execute );
939 }
940 }
941 const QModelIndex rowSourceIndex = mMasterModel->index( masterIndex.row(), 0 );
942 if ( !rowSourceIndex.isValid() )
943 {
944 return;
945 }
946
947 //add actions from QgsMapLayerActionRegistry to context menu
948 QgsMapLayerActionContext context;
949 const QList<QgsMapLayerAction *> registeredActions = QgsGui::mapLayerActionRegistry()->mapLayerActions( mLayer, Qgis::MapLayerActionTarget::Layer | Qgis::MapLayerActionTarget::SingleFeature, context );
950 if ( !registeredActions.isEmpty() )
951 {
952 //add a separator between user defined and standard actions
953 menu->addSeparator();
954
955 for ( QgsMapLayerAction *action : registeredActions )
956 {
957 QgsAttributeTableMapLayerAction *a = new QgsAttributeTableMapLayerAction( action->text(), this, action, rowSourceIndex );
958 menu->addAction( action->text(), a, &QgsAttributeTableMapLayerAction::execute );
959 }
960 }
961
962 // entries for multiple features layer actions
963 // only show if the context menu is shown over a selected row
964 const QgsFeatureId currentFid = mMasterModel->rowToId( masterIndex.row() );
965 if ( mLayer->selectedFeatureCount() > 1 && mLayer->selectedFeatureIds().contains( currentFid ) )
966 {
967 const QList<QgsMapLayerAction *> registeredActions = QgsGui::mapLayerActionRegistry()->mapLayerActions( mLayer, Qgis::MapLayerActionTarget::MultipleFeatures, context );
968 if ( !registeredActions.isEmpty() )
969 {
970 menu->addSeparator();
971 QAction *action = menu->addAction( tr( "Actions on Selection (%1)" ).arg( mLayer->selectedFeatureCount() ) );
972 action->setEnabled( false );
973
974 QgsMapLayerActionContext context;
975 for ( QgsMapLayerAction *action : registeredActions )
976 {
977 menu->addAction( action->text(), action, [this, action, context]() {
978 Q_NOWARN_DEPRECATED_PUSH
979 action->triggerForFeatures( mLayer, mLayer->selectedFeatures() );
980 Q_NOWARN_DEPRECATED_POP
981 action->triggerForFeatures( mLayer, mLayer->selectedFeatures(), context );
982 } );
983 }
984 }
985 }
986
987 menu->addSeparator();
988 QgsAttributeTableAction *a = new QgsAttributeTableAction( tr( "Open Form" ), this, QUuid(), rowSourceIndex );
989 menu->addAction( tr( "Open Form…" ), a, &QgsAttributeTableAction::featureForm );
990}
991
992
993void QgsDualView::widgetWillShowContextMenu( QgsActionMenu *menu, const QModelIndex &atIndex )
994{
995 emit showContextMenuExternally( menu, mFilterModel->rowToId( atIndex ) );
996}
997
998
999void QgsDualView::showViewHeaderMenu( QPoint point )
1000{
1001 const int col = mTableView->columnAt( point.x() );
1002
1003 delete mHorizontalHeaderMenu;
1004 mHorizontalHeaderMenu = new QMenu( this );
1005
1006 QAction *hide = new QAction( tr( "&Hide Column" ), mHorizontalHeaderMenu );
1007 connect( hide, &QAction::triggered, this, &QgsDualView::hideColumn );
1008 hide->setData( col );
1009 mHorizontalHeaderMenu->addAction( hide );
1010 QAction *setWidth = new QAction( tr( "&Set Width…" ), mHorizontalHeaderMenu );
1011 connect( setWidth, &QAction::triggered, this, &QgsDualView::resizeColumn );
1012 setWidth->setData( col );
1013 mHorizontalHeaderMenu->addAction( setWidth );
1014
1015 QAction *setWidthAllColumns = new QAction( tr( "&Set All Column Widths…" ), mHorizontalHeaderMenu );
1016 connect( setWidthAllColumns, &QAction::triggered, this, &QgsDualView::resizeAllColumns );
1017 setWidthAllColumns->setData( col );
1018 mHorizontalHeaderMenu->addAction( setWidthAllColumns );
1019
1020 QAction *optimizeWidth = new QAction( tr( "&Autosize" ), mHorizontalHeaderMenu );
1021 connect( optimizeWidth, &QAction::triggered, this, &QgsDualView::autosizeColumn );
1022 optimizeWidth->setData( col );
1023 mHorizontalHeaderMenu->addAction( optimizeWidth );
1024
1025 QAction *optimizeWidthAllColumns = new QAction( tr( "&Autosize All Columns" ), mHorizontalHeaderMenu );
1026 connect( optimizeWidthAllColumns, &QAction::triggered, this, &QgsDualView::autosizeAllColumns );
1027 mHorizontalHeaderMenu->addAction( optimizeWidthAllColumns );
1028
1029
1030 mHorizontalHeaderMenu->addSeparator();
1031 QAction *organize = new QAction( tr( "&Organize Columns…" ), mHorizontalHeaderMenu );
1032 connect( organize, &QAction::triggered, this, &QgsDualView::organizeColumns );
1033 mHorizontalHeaderMenu->addAction( organize );
1034 QAction *sort = new QAction( tr( "&Sort…" ), mHorizontalHeaderMenu );
1035 connect( sort, &QAction::triggered, this, [this]() { modifySort(); } );
1036 mHorizontalHeaderMenu->addAction( sort );
1037
1038 mConfig.update( mLayer->fields() );
1039 // get layer field index from column name
1040 int fieldIndex = -1;
1041 if ( col != -1 )
1042 fieldIndex = mLayer->fields().indexFromName( mConfig.columns().at( mConfig.mapVisibleColumnToIndex( col ) ).name );
1043 const Qgis::FieldOrigin fieldOrigin = mLayer->fields().fieldOrigin( fieldIndex );
1044
1045 mHorizontalHeaderMenu->addSeparator();
1046
1047 const QgsVectorLayer *vl = mFilterModel->layer();
1048 const QgsVectorDataProvider *dataProvider = vl->dataProvider();
1049 const Qgis::VectorProviderCapabilities caps = dataProvider->capabilities();
1050 const bool layerIsReadOnly { vl->readOnly() };
1051 const bool canChangeAttributeValue = !layerIsReadOnly && ( caps & Qgis::VectorProviderCapability::ChangeAttributeValues );
1052
1053 bool fieldCalculatorEnabled = false;
1054
1055 switch ( fieldOrigin )
1056 {
1059 fieldCalculatorEnabled = canChangeAttributeValue;
1060 break;
1062 {
1063 int srcFieldIndex;
1064 const QgsVectorLayerJoinInfo *info = mLayer->joinBuffer()->joinForFieldIndex( fieldIndex, mLayer->fields(), srcFieldIndex );
1065 const QgsVectorLayer *joinedLayer = info->joinLayer();
1066 const QgsVectorDataProvider *joinedDataProvider = joinedLayer->dataProvider();
1067 const Qgis::VectorProviderCapabilities joinedCaps = joinedDataProvider->capabilities();
1068 const bool joinedLayerIsReadOnly { joinedLayer->readOnly() };
1069 const bool joinedLayerCanChangeAttributeValue = !joinedLayerIsReadOnly && ( joinedCaps & Qgis::VectorProviderCapability::ChangeAttributeValues );
1070 if ( info && info->isEditable() )
1071 fieldCalculatorEnabled = joinedLayerCanChangeAttributeValue;
1072 break;
1073 }
1076 break;
1077 default:
1078 break;
1079 }
1080
1081 QAction *fieldCalculator = new QAction( tr( "Open &Field Calculator…" ), mHorizontalHeaderMenu );
1082 connect( fieldCalculator, &QAction::triggered, this, &QgsDualView::fieldCalculator );
1083 fieldCalculator->setData( fieldIndex );
1084 mHorizontalHeaderMenu->addAction( fieldCalculator );
1085
1086 fieldCalculator->setEnabled( fieldCalculatorEnabled );
1087
1088 mHorizontalHeaderMenu->popup( mTableView->horizontalHeader()->mapToGlobal( point ) );
1089}
1090
1091void QgsDualView::organizeColumns()
1092{
1093 if ( !mLayer )
1094 {
1095 return;
1096 }
1097
1098 QgsOrganizeTableColumnsDialog dialog( mLayer, attributeTableConfig(), this );
1099 if ( dialog.exec() == QDialog::Accepted )
1100 {
1101 const QgsAttributeTableConfig config = dialog.config();
1102 setAttributeTableConfig( config );
1103 }
1104}
1105
1106void QgsDualView::tableColumnResized( int column, int width )
1107{
1108 QgsAttributeTableConfig config = mConfig;
1109 const int sourceCol = config.mapVisibleColumnToIndex( column );
1110 if ( sourceCol >= 0 && config.columnWidth( sourceCol ) != width )
1111 {
1112 config.setColumnWidth( sourceCol, width );
1113 setAttributeTableConfig( config );
1114 }
1115}
1116
1117void QgsDualView::hideColumn()
1118{
1119 QAction *action = qobject_cast<QAction *>( sender() );
1120 const int col = action->data().toInt();
1121 QgsAttributeTableConfig config = mConfig;
1122 const int sourceCol = mConfig.mapVisibleColumnToIndex( col );
1123 if ( sourceCol >= 0 )
1124 {
1125 config.setColumnHidden( sourceCol, true );
1126 setAttributeTableConfig( config );
1127 }
1128}
1129
1130void QgsDualView::fieldCalculator()
1131{
1132 QAction *action = qobject_cast<QAction *>( sender() );
1133 const int fieldIndex = action->data().toInt();
1134 mConfig.update( mLayer->fields() );
1135 QgsFieldCalculator calc( mLayer, this, fieldIndex );
1136 if ( calc.exec() == QDialog::Accepted )
1137 {
1138 int col = mMasterModel->fieldCol( calc.changedAttributeId() );
1139
1140 if ( col >= 0 )
1141 mMasterModel->reload( mMasterModel->index( 0, col ), mMasterModel->index( mMasterModel->rowCount() - 1, col ) );
1142 }
1143}
1144
1145void QgsDualView::resizeColumn()
1146{
1147 QAction *action = qobject_cast<QAction *>( sender() );
1148 const int col = action->data().toInt();
1149 if ( col < 0 )
1150 return;
1151
1152 QgsAttributeTableConfig config = mConfig;
1153 const int sourceCol = config.mapVisibleColumnToIndex( col );
1154 if ( sourceCol >= 0 )
1155 {
1156 bool ok = false;
1157 const int width = QInputDialog::getInt( this, tr( "Set column width" ), tr( "Enter column width" ), mTableView->columnWidth( col ), 0, 1000, 10, &ok );
1158 if ( ok )
1159 {
1160 config.setColumnWidth( sourceCol, width );
1161 setAttributeTableConfig( config );
1162 }
1163 }
1164}
1165
1166void QgsDualView::resizeAllColumns()
1167{
1168 QAction *action = qobject_cast<QAction *>( sender() );
1169 const int col = action->data().toInt();
1170 if ( col < 0 )
1171 return;
1172
1173 QgsAttributeTableConfig config = mConfig;
1174
1175 bool ok = false;
1176 const int width = QInputDialog::getInt( this, tr( "Set Column Width" ), tr( "Enter column width" ), mTableView->columnWidth( col ), 1, 1000, 10, &ok );
1177 if ( ok )
1178 {
1179 const int colCount = mTableView->model()->columnCount();
1180 if ( colCount > 0 )
1181 {
1182 for ( int i = 0; i < colCount; i++ )
1183 {
1184 config.setColumnWidth( i, width );
1185 }
1186 setAttributeTableConfig( config );
1187 }
1188 }
1189}
1190
1191void QgsDualView::autosizeColumn()
1192{
1193 QAction *action = qobject_cast<QAction *>( sender() );
1194 const int col = action->data().toInt();
1195 mTableView->resizeColumnToContents( col );
1196}
1197
1198void QgsDualView::autosizeAllColumns()
1199{
1200 mTableView->resizeColumnsToContents();
1201}
1202
1203bool QgsDualView::modifySort()
1204{
1205 if ( !mLayer )
1206 return false;
1207
1208 QgsAttributeTableConfig config = mConfig;
1209
1210 QDialog orderByDlg;
1211 orderByDlg.setWindowTitle( tr( "Configure Attribute Table Sort Order" ) );
1212 QDialogButtonBox *dialogButtonBox = new QDialogButtonBox( QDialogButtonBox::Ok | QDialogButtonBox::Cancel );
1213 QGridLayout *layout = new QGridLayout();
1214 connect( dialogButtonBox, &QDialogButtonBox::accepted, &orderByDlg, &QDialog::accept );
1215 connect( dialogButtonBox, &QDialogButtonBox::rejected, &orderByDlg, &QDialog::reject );
1216 orderByDlg.setLayout( layout );
1217
1218 QGroupBox *sortingGroupBox = new QGroupBox();
1219 sortingGroupBox->setTitle( tr( "Defined sort order in attribute table" ) );
1220 sortingGroupBox->setCheckable( true );
1221 sortingGroupBox->setChecked( !sortExpression().isEmpty() );
1222 layout->addWidget( sortingGroupBox );
1223 sortingGroupBox->setLayout( new QGridLayout() );
1224
1225 QgsExpressionBuilderWidget *expressionBuilder = new QgsExpressionBuilderWidget();
1226 const QgsExpressionContext context( QgsExpressionContextUtils::globalProjectLayerScopes( mLayer ) );
1227
1228 expressionBuilder->initWithLayer( mLayer, context, u"generic"_s );
1229 expressionBuilder->setExpressionText( sortExpression().isEmpty() ? mLayer->displayExpression() : sortExpression() );
1230
1231 sortingGroupBox->layout()->addWidget( expressionBuilder );
1232
1233 QCheckBox *cbxSortAscending = new QCheckBox( tr( "Sort ascending" ) );
1234 cbxSortAscending->setChecked( config.sortOrder() == Qt::AscendingOrder );
1235 sortingGroupBox->layout()->addWidget( cbxSortAscending );
1236
1237 layout->addWidget( dialogButtonBox );
1238 if ( orderByDlg.exec() )
1239 {
1240 const Qt::SortOrder sortOrder = cbxSortAscending->isChecked() ? Qt::AscendingOrder : Qt::DescendingOrder;
1241 if ( sortingGroupBox->isChecked() )
1242 {
1243 setSortExpression( expressionBuilder->expressionText(), sortOrder );
1244 config.setSortExpression( expressionBuilder->expressionText() );
1245 config.setSortOrder( sortOrder );
1246 }
1247 else
1248 {
1249 setSortExpression( QString(), sortOrder );
1250 config.setSortExpression( QString() );
1251 }
1252
1253 return true;
1254 }
1255 else
1256 {
1257 return false;
1258 }
1259}
1260
1262{
1263 QSet<int> attributes;
1264
1265 const QgsAttributeTableConfig config { layer->attributeTableConfig() };
1266
1267 const QVector<QgsAttributeTableConfig::ColumnConfig> constColumnconfigs { config.columns() };
1268 for ( const QgsAttributeTableConfig::ColumnConfig &columnConfig : std::as_const( constColumnconfigs ) )
1269 {
1270 if ( columnConfig.type == QgsAttributeTableConfig::Type::Field && !columnConfig.hidden )
1271 {
1272 attributes.insert( layer->fields().lookupField( columnConfig.name ) );
1273 }
1274 }
1275
1276 const QSet<int> colAttrs { attributes };
1277 for ( const int attrIdx : std::as_const( colAttrs ) )
1278 {
1279 if ( layer->fields().fieldOrigin( attrIdx ) == Qgis::FieldOrigin::Expression )
1280 {
1281 attributes += QgsExpression( layer->expressionField( attrIdx ) ).referencedAttributeIndexes( layer->fields() );
1282 }
1283 }
1284
1285 QgsAttributeList attrs { attributes.values() };
1286 std::sort( attrs.begin(), attrs.end() );
1287 return attrs;
1288}
1289
1290void QgsDualView::zoomToCurrentFeature()
1291{
1292 const QModelIndex currentIndex = mTableView->currentIndex();
1293 if ( !currentIndex.isValid() )
1294 {
1295 return;
1296 }
1297
1298 QgsFeatureIds ids;
1299 ids.insert( mFilterModel->rowToId( currentIndex ) );
1300 QgsMapCanvas *canvas = mFilterModel->mapCanvas();
1301 if ( canvas )
1302 {
1303 canvas->zoomToFeatureIds( mLayer, ids );
1304 }
1305}
1306
1307void QgsDualView::panToCurrentFeature()
1308{
1309 const QModelIndex currentIndex = mTableView->currentIndex();
1310 if ( !currentIndex.isValid() )
1311 {
1312 return;
1313 }
1314
1315 QgsFeatureIds ids;
1316 ids.insert( mFilterModel->rowToId( currentIndex ) );
1317 QgsMapCanvas *canvas = mFilterModel->mapCanvas();
1318 if ( canvas )
1319 {
1320 canvas->panToFeatureIds( mLayer, ids );
1321 }
1322}
1323
1324void QgsDualView::flashCurrentFeature()
1325{
1326 const QModelIndex currentIndex = mTableView->currentIndex();
1327 if ( !currentIndex.isValid() )
1328 {
1329 return;
1330 }
1331
1332 QgsFeatureIds ids;
1333 ids.insert( mFilterModel->rowToId( currentIndex ) );
1334 QgsMapCanvas *canvas = mFilterModel->mapCanvas();
1335 if ( canvas )
1336 {
1337 canvas->flashFeatureIds( mLayer, ids );
1338 }
1339}
1340
1341void QgsDualView::rebuildFullLayerCache()
1342{
1343 connect( mLayerCache, &QgsVectorLayerCache::progress, this, &QgsDualView::progress, Qt::UniqueConnection );
1344 connect( mLayerCache, &QgsVectorLayerCache::finished, this, &QgsDualView::finished, Qt::UniqueConnection );
1345
1346 mLayerCache->setFullCache( true );
1347}
1348
1349void QgsDualView::previewExpressionChanged( const QString &expression )
1350{
1351 mLayer->setDisplayExpression( expression );
1352}
1353
1354void QgsDualView::onSortColumnChanged()
1355{
1356 QgsAttributeTableConfig cfg = attributeTableConfig();
1357 if ( cfg.sortExpression() != mFilterModel->sortExpression() || cfg.sortOrder() != mFilterModel->sortOrder() )
1358 {
1359 cfg.setSortExpression( mFilterModel->sortExpression() );
1360 cfg.setSortOrder( mFilterModel->sortOrder() );
1362 }
1363}
1364
1365void QgsDualView::updateSelectedFeatures()
1366{
1367 QgsFeatureRequest r = mMasterModel->request();
1369 return; // already requested all features
1370
1371 r.setFilterFids( masterModel()->layer()->selectedFeatureIds() );
1372 mMasterModel->setRequest( r );
1373 mMasterModel->loadLayer();
1374 emit filterChanged();
1375}
1376
1377void QgsDualView::updateEditedAddedFeatures()
1378{
1379 QgsFeatureRequest r = mMasterModel->request();
1381 return; // already requested all features
1382
1383 r.setFilterFids( masterModel()->layer()->editBuffer() ? masterModel()->layer()->editBuffer()->allAddedOrEditedFeatures() : QgsFeatureIds() );
1384 mMasterModel->setRequest( r );
1385 mMasterModel->loadLayer();
1386 emit filterChanged();
1387}
1388
1389void QgsDualView::extentChanged()
1390{
1391 QgsFeatureRequest r = mMasterModel->request();
1392 if ( mFilterModel->mapCanvas() && ( r.filterType() != Qgis::FeatureRequestFilterType::NoFilter || !r.filterRect().isNull() ) )
1393 {
1394 const QgsRectangle rect = mFilterModel->mapCanvas()->mapSettings().mapToLayerCoordinates( mLayer, mFilterModel->mapCanvas()->extent() );
1395 r.setFilterRect( rect );
1396 mMasterModel->setRequest( r );
1397 mMasterModel->loadLayer();
1398 }
1399 emit filterChanged();
1400}
1401
1402void QgsDualView::featureFormAttributeChanged( const QString &attribute, const QVariant &value, bool attributeChanged )
1403{
1404 Q_UNUSED( attribute )
1405 Q_UNUSED( value )
1406 if ( attributeChanged )
1407 {
1408 mFeatureListView->setCurrentFeatureEdited( true );
1409 mAttributeForm->save();
1410 }
1411}
1412
1414{
1415 mFilterModel->setFilteredFeatures( filteredFeatures );
1416}
1417
1418void QgsDualView::filterFeatures( const QgsExpression &filterExpression, const QgsExpressionContext &context )
1419{
1420 mFilterModel->setFilterExpression( filterExpression, context );
1421 mFilterModel->filterFeatures();
1422}
1423
1424
1426{
1427 mMasterModel->setRequest( request );
1428}
1429
1431{
1432 mTableView->setFeatureSelectionManager( featureSelectionManager );
1433 mFeatureListView->setFeatureSelectionManager( featureSelectionManager );
1434
1435 if ( mFeatureSelectionManager && mFeatureSelectionManager->parent() == this )
1436 delete mFeatureSelectionManager;
1437
1438 mFeatureSelectionManager = featureSelectionManager;
1439}
1440
1442{
1443 mConfig = config;
1444 mConfig.update( mLayer->fields() );
1445 mLayer->setAttributeTableConfig( mConfig );
1446 mFilterModel->setAttributeTableConfig( mConfig );
1447 mTableView->setAttributeTableConfig( mConfig );
1448 const QgsAttributeList attributes { requiredAttributes( mLayer ) };
1449 QgsFeatureRequest request = mMasterModel->request();
1450 // if the sort expression needs geometry reset the flag
1451 if ( QgsExpression( config.sortExpression() ).needsGeometry() )
1452 {
1453 mLayerCache->setCacheGeometry( true );
1454 request.setFlags( request.flags().setFlag( Qgis::FeatureRequestFlag::NoGeometry, false ) );
1455 }
1456 request.setSubsetOfAttributes( attributes );
1457 mMasterModel->setRequest( request );
1458 mLayerCache->setCacheSubsetOfAttributes( attributes );
1459}
1460
1461void QgsDualView::setSortExpression( const QString &sortExpression, Qt::SortOrder sortOrder )
1462{
1463 if ( sortExpression.isNull() )
1464 mFilterModel->sort( -1 );
1465 else
1466 mFilterModel->sort( sortExpression, sortOrder );
1467
1468 mConfig.setSortExpression( sortExpression );
1469 mConfig.setSortOrder( sortOrder );
1470 setAttributeTableConfig( mConfig );
1471}
1472
1474{
1475 return mFilterModel->sortExpression();
1476}
1477
1479{
1480 return mConfig;
1481}
1482
1483void QgsDualView::progress( int i, bool &cancel )
1484{
1485 if ( !mProgressDlg )
1486 {
1487 mProgressDlg = new QProgressDialog( tr( "Loading features…" ), tr( "Abort" ), 0, 0, this );
1488 mProgressDlg->setWindowTitle( tr( "Attribute Table" ) );
1489 mProgressDlg->setWindowModality( Qt::WindowModal );
1490 mProgressDlg->show();
1491 }
1492
1493 mProgressDlg->setLabelText( tr( "%L1 features loaded." ).arg( i ) );
1494 QCoreApplication::processEvents();
1495
1496 cancel = mProgressDlg && mProgressDlg->wasCanceled();
1497}
1498
1499void QgsDualView::finished()
1500{
1501 delete mProgressDlg;
1502 mProgressDlg = nullptr;
1503}
1504
1505/*
1506 * QgsAttributeTableAction
1507 */
1508
1510{
1511 mDualView->masterModel()->executeAction( mAction, mFieldIdx );
1512}
1513
1515{
1516 QgsFeatureIds editedIds;
1517 editedIds << mDualView->masterModel()->rowToId( mFieldIdx.row() );
1518 mDualView->setCurrentEditSelection( editedIds );
1519 mDualView->setView( QgsDualView::AttributeEditor );
1520}
1521
1522/*
1523 * QgsAttributeTableMapLayerAction
1524 */
1525
1527{
1529 mDualView->masterModel()->executeMapLayerAction( mAction, mFieldIdx, context );
1530}
@ SelectAtId
Fast access to features using their ID.
Definition qgis.h:533
@ ChangeAttributeValues
Allows modification of attribute values.
Definition qgis.h:529
@ NoFilter
No filter is applied.
Definition qgis.h:2358
@ NoGeometry
Geometry is not required. It may still be returned if e.g. required for a filter condition.
Definition qgis.h:2330
@ NoFilter
No spatial filtering of features.
Definition qgis.h:2387
@ MultipleFeatures
Action targets multiple features from a layer.
Definition qgis.h:5052
@ Layer
Action targets a complete layer.
Definition qgis.h:5050
@ SingleFeature
Action targets a single feature from a layer.
Definition qgis.h:5051
QFlags< VectorProviderCapability > VectorProviderCapabilities
Vector data provider capabilities.
Definition qgis.h:564
FieldOrigin
Field origin.
Definition qgis.h:1825
@ Provider
Field originates from the underlying data provider of the vector layer.
Definition qgis.h:1827
@ Edit
Field has been temporarily added in editing mode.
Definition qgis.h:1829
@ Unknown
The field origin has not been specified.
Definition qgis.h:1826
@ Expression
Field is calculated from an expression.
Definition qgis.h:1830
@ Join
Field originates from a joined layer.
Definition qgis.h:1828
A menu that is populated automatically with the actions defined for a given layer.
static QIcon getThemeIcon(const QString &name, const QColor &fillColor=QColor(), const QColor &strokeColor=QColor())
Helper to get a theme icon.
Contains context information for attribute editor widgets.
@ 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
The attribute form widget for vector layer features.
void refreshFeature()
reload current feature
bool needsGeometry() const
Returns true if any of the form widgets need feature geometry.
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.
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.
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 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).
void sortColumnChanged(int column, Qt::SortOrder order)
Emitted whenever the sort column is changed.
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 progress(int i, bool &cancel)
void finished()
Emitted when the model has completely loaded all features.
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:64
@ AttributeEditor
Show a list of the features, where one can be chosen and the according attribute dialog will be prese...
Definition qgsdualview.h:76
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.
static const QgsSettingsEntryBool * settingsFeatureListHighlightFeature
Definition qgsdualview.h:54
static const QgsSettingsEntryInteger * settingsAttributeTableRowCache
Definition qgsdualview.h:56
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:83
@ NoAction
No action is done.
Definition qgsdualview.h:84
@ PanToFeature
The map is panned to the center of the feature bounding-box.
Definition qgsdualview.h:85
@ ZoomToFeature
The map is zoomed to contained the feature bounding-box.
Definition qgsdualview.h:86
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.
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=u"generic"_s, 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...
Handles 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.
@ FeatureRole
Feature with all attributes and no geometry.
Shows a list of features and renders an 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)
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:60
QgsFeatureId id
Definition qgsfeature.h:63
A widget for customizing conditional formatting options.
void rulesUpdated(const QString &fieldName)
Emitted when the conditional styling rules are updated.
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:109
static QgsShortcutsManager * shortcutsManager()
Returns the global shortcuts manager, used for managing a QAction and QShortcut sequences.
Definition qgsgui.cpp:139
static QgsMapLayerActionRegistry * mapLayerActionRegistry()
Returns the global map layer action registry, used for registering map layer actions.
Definition qgsgui.cpp:149
Is an interface class to abstract feature selection handling.
static long zoomToMatchingFeatures(QgsMapCanvas *canvas, QgsVectorLayer *layer, const QString &filter)
Zooms a map canvas to features from the specified layer which match the given filter expression strin...
static long flashMatchingFeatures(QgsMapCanvas *canvas, QgsVectorLayer *layer, const QString &filter)
Flashes features from the specified layer which match the given filter expression string with a map c...
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.
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.
void layerModified()
Emitted when modifications has been done on layer.
A rectangle specified with double values.
A QScrollArea subclass with improved scrolling behavior.
T value(const QString &dynamicKeyPart=QString()) const
Returns settings value.
A boolean settings entry.
An integer settings entry.
A variant settings entry.
static QgsSettingsTreeNode * sTreeAttributeTable
static QgsSettingsTreeNode * sTreeWindowState
Stores settings for use within QGIS.
Definition qgssettings.h:68
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.
virtual Q_INVOKABLE Qgis::VectorProviderCapabilities capabilities() const
Returns flags containing the supported capabilities.
Caches features for a given QgsVectorLayer.
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.
bool isEditable() const
Returns whether joined fields may be edited through the form of the target layer.
QgsVectorLayer * joinLayer() const
Returns joined layer (may be nullptr if the reference was set by layer ID and not resolved yet).
Represents a vector layer which manages a vector based dataset.
bool isEditable() const final
Returns true if the provider is in editing mode.
bool isSpatial() const final
Returns true if this is a geometry layer and false in case of NoGeometry (table only) or UnknownGeome...
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.
QgsVectorDataProvider * dataProvider() final
Returns the layer's data provider, it may be nullptr.
QgsSignalBlocker< Object > whileBlocking(Object *object)
Temporarily blocks signals from a QObject while calling a single method from the object.
Definition qgis.h:7234
QSet< QgsFeatureId > QgsFeatureIds
qint64 QgsFeatureId
64 bit feature ids negative numbers are used for uncommitted/newly added features
QList< int > QgsAttributeList
Definition qgsfield.h:30
Defines the configuration of a column in the attribute table.