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