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