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