QGIS API Documentation  2.99.0-Master (314842d)
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 "qgsapplication.h"
17 #include "qgsactionmanager.h"
18 #include "qgsattributetablemodel.h"
19 #include "qgsdualview.h"
21 #include "qgsfeaturelistmodel.h"
23 #include "qgsmapcanvas.h"
25 #include "qgsmessagelog.h"
26 #include "qgsvectordataprovider.h"
27 #include "qgsvectorlayercache.h"
30 #include "qgssettings.h"
31 #include "qgsscrollarea.h"
32 
33 #include <QClipboard>
34 #include <QDialog>
35 #include <QMenu>
36 #include <QMessageBox>
37 #include <QProgressDialog>
38 #include <QGroupBox>
39 #include <QInputDialog>
40 
41 QgsDualView::QgsDualView( QWidget *parent )
42  : QStackedWidget( parent )
43  , mEditorContext()
44  , mMasterModel( nullptr )
45  , mFilterModel( nullptr )
46  , mFeatureListModel( nullptr )
47  , mAttributeForm( nullptr )
48  , mHorizontalHeaderMenu( nullptr )
49  , mLayerCache( nullptr )
50  , mProgressDlg( nullptr )
51  , mFeatureSelectionManager( nullptr )
52  , mAttributeEditorScrollArea( nullptr )
53 {
54  setupUi( this );
55 
56  mConditionalFormatWidget->hide();
57 
58  mPreviewActionMapper = new QSignalMapper( this );
59 
60  mPreviewColumnsMenu = new QMenu( this );
61  mActionPreviewColumnsMenu->setMenu( mPreviewColumnsMenu );
62 
63  // Set preview icon
64  mActionExpressionPreview->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mIconExpressionPreview.svg" ) ) );
65 
66  // Connect layer list preview signals
67  connect( mActionExpressionPreview, &QAction::triggered, this, &QgsDualView::previewExpressionBuilder );
68  connect( mPreviewActionMapper, static_cast < void ( QSignalMapper::* )( QObject * ) > ( &QSignalMapper::mapped ), this, &QgsDualView::previewColumnChanged );
69  connect( mFeatureList, &QgsFeatureListView::displayExpressionChanged, this, &QgsDualView::previewExpressionChanged );
70 }
71 
72 void QgsDualView::init( QgsVectorLayer *layer, QgsMapCanvas *mapCanvas, const QgsFeatureRequest &request, const QgsAttributeEditorContext &context, bool loadFeatures )
73 {
74  mMapCanvas = mapCanvas;
75 
76  if ( !layer )
77  return;
78 
79  mLayer = layer;
80 
81  mEditorContext = context;
82 
83  connect( mTableView, &QgsAttributeTableView::willShowContextMenu, this, &QgsDualView::viewWillShowContextMenu );
84  mTableView->horizontalHeader()->setContextMenuPolicy( Qt::CustomContextMenu );
85  connect( mTableView->horizontalHeader(), &QHeaderView::customContextMenuRequested, this, &QgsDualView::showViewHeaderMenu );
86  connect( mTableView, &QgsAttributeTableView::columnResized, this, &QgsDualView::tableColumnResized );
87 
88  initLayerCache( !( request.flags() & QgsFeatureRequest::NoGeometry ) || !request.filterRect().isNull() );
89  initModels( mapCanvas, request, loadFeatures );
90 
91  mConditionalFormatWidget->setLayer( mLayer );
92 
93  mTableView->setModel( mFilterModel );
94  mFeatureList->setModel( mFeatureListModel );
95  delete mAttributeForm;
96  mAttributeForm = new QgsAttributeForm( mLayer, QgsFeature(), mEditorContext );
97  if ( !context.parentContext() )
98  {
99  mAttributeEditorScrollArea = new QgsScrollArea();
100  mAttributeEditorScrollArea->setWidgetResizable( true );
101  mAttributeEditor->layout()->addWidget( mAttributeEditorScrollArea );
102  mAttributeEditorScrollArea->setWidget( mAttributeForm );
103  }
104  else
105  {
106  mAttributeEditor->layout()->addWidget( mAttributeForm );
107  }
108 
109  connect( mAttributeForm, &QgsAttributeForm::attributeChanged, this, &QgsDualView::featureFormAttributeChanged );
110  connect( mAttributeForm, &QgsAttributeForm::modeChanged, this, &QgsDualView::formModeChanged );
111  connect( mMasterModel, &QgsAttributeTableModel::modelChanged, mAttributeForm, &QgsAttributeForm::refreshFeature );
113  connect( mFilterModel, &QgsAttributeTableFilterModel::sortColumnChanged, this, &QgsDualView::onSortColumnChanged );
114  if ( mFeatureListPreviewButton->defaultAction() )
115  mFeatureList->setDisplayExpression( mDisplayExpression );
116  else
117  columnBoxInit();
118 
119  // This slows down load of the attribute table heaps and uses loads of memory.
120  //mTableView->resizeColumnsToContents();
121 
122  mFeatureList->setEditSelection( QgsFeatureIds() << mFeatureListModel->idxToFid( mFeatureListModel->index( 0, 0 ) ) );
123 }
124 
126 {
127  // load fields
128  QList<QgsField> fields = mLayer->fields().toList();
129 
130  QString defaultField;
131 
132  // default expression: saved value
133  QString displayExpression = mLayer->displayExpression();
134 
135  if ( displayExpression.isEmpty() )
136  {
137  // ... there isn't really much to display
138  displayExpression = QStringLiteral( "'[Please define preview text]'" );
139  }
140 
141  mFeatureListPreviewButton->addAction( mActionExpressionPreview );
142  mFeatureListPreviewButton->addAction( mActionPreviewColumnsMenu );
143 
144  Q_FOREACH ( const QgsField &field, fields )
145  {
146  int fieldIndex = mLayer->fields().lookupField( field.name() );
147  if ( fieldIndex == -1 )
148  continue;
149 
150  if ( QgsEditorWidgetRegistry::instance()->findBest( mLayer, field.name() ).type() != QLatin1String( "Hidden" ) )
151  {
152  QIcon icon = mLayer->fields().iconForField( fieldIndex );
153  QString text = field.name();
154 
155  // Generate action for the preview popup button of the feature list
156  QAction *previewAction = new QAction( icon, text, mFeatureListPreviewButton );
157  mPreviewActionMapper->setMapping( previewAction, previewAction );
158  connect( previewAction, &QAction::triggered, this, [previewAction, this]( bool ) { this->mPreviewActionMapper->map( previewAction ); }
159  );
160  mPreviewColumnsMenu->addAction( previewAction );
161 
162  if ( text == defaultField )
163  {
164  mFeatureListPreviewButton->setDefaultAction( previewAction );
165  }
166  }
167  }
168 
169  // If there is no single field found as preview
170  if ( !mFeatureListPreviewButton->defaultAction() )
171  {
172  mFeatureList->setDisplayExpression( displayExpression );
173  mFeatureListPreviewButton->setDefaultAction( mActionExpressionPreview );
174  mDisplayExpression = mFeatureList->displayExpression();
175  }
176  else
177  {
178  mFeatureListPreviewButton->defaultAction()->trigger();
179  }
180 
181  QAction *sortByPreviewExpression = new QAction( QgsApplication::getThemeIcon( QStringLiteral( "sort.svg" ) ), tr( "Sort by preview expression" ), this );
182  connect( sortByPreviewExpression, &QAction::triggered, this, &QgsDualView::sortByPreviewExpression );
183  mFeatureListPreviewButton->addAction( sortByPreviewExpression );
184 }
185 
187 {
188  setCurrentIndex( view );
189 }
190 
192 {
193  return static_cast< QgsDualView::ViewMode >( currentIndex() );
194 }
195 
197 {
198  // cleanup any existing connections
199  switch ( mFilterModel->filterMode() )
200  {
202  disconnect( mMapCanvas, &QgsMapCanvas::extentsChanged, this, &QgsDualView::extentChanged );
203  break;
204 
208  break;
209 
211  disconnect( masterModel()->layer(), &QgsVectorLayer::selectionChanged, this, &QgsDualView::updateSelectedFeatures );
212  break;
213  }
214 
215  QgsFeatureRequest r = mMasterModel->request();
216  bool needsGeometry = filterMode == QgsAttributeTableFilterModel::ShowVisible;
217 
218  bool requiresTableReload = ( r.filterType() != QgsFeatureRequest::FilterNone || !r.filterRect().isNull() ) // previous request was subset
219  || ( needsGeometry && r.flags() & QgsFeatureRequest::NoGeometry ) // no geometry for last request
220  || ( mMasterModel->rowCount() == 0 ); // no features
221 
222  if ( !needsGeometry )
224  else
228  r.disableFilter();
229 
230  // setup new connections and filter request parameters
231  switch ( filterMode )
232  {
234  connect( mMapCanvas, &QgsMapCanvas::extentsChanged, this, &QgsDualView::extentChanged );
235  if ( mMapCanvas )
236  {
237  QgsRectangle rect = mMapCanvas->mapSettings().mapToLayerCoordinates( mLayer, mMapCanvas->extent() );
238  r.setFilterRect( rect );
239  }
240  break;
241 
245  break;
246 
248  connect( masterModel()->layer(), &QgsVectorLayer::selectionChanged, this, &QgsDualView::updateSelectedFeatures );
249  r.setFilterFids( masterModel()->layer()->selectedFeatureIds() );
250  break;
251  }
252 
253  if ( requiresTableReload )
254  {
255  mMasterModel->setRequest( r );
256  whileBlocking( mLayerCache )->setCacheGeometry( needsGeometry );
257  mMasterModel->loadLayer();
258  }
259 
260  //update filter model
261  mFilterModel->setFilterMode( filterMode );
262  emit filterChanged();
263 }
264 
265 void QgsDualView::setSelectedOnTop( bool selectedOnTop )
266 {
267  mFilterModel->setSelectedOnTop( selectedOnTop );
268 }
269 
270 void QgsDualView::initLayerCache( bool cacheGeometry )
271 {
272  // Initialize the cache
273  QgsSettings settings;
274  int cacheSize = settings.value( QStringLiteral( "qgis/attributeTableRowCache" ), "10000" ).toInt();
275  mLayerCache = new QgsVectorLayerCache( mLayer, cacheSize, this );
276  mLayerCache->setCacheGeometry( cacheGeometry );
277  if ( 0 == cacheSize || 0 == ( QgsVectorDataProvider::SelectAtId & mLayer->dataProvider()->capabilities() ) )
278  {
279  connect( mLayerCache, &QgsVectorLayerCache::invalidated, this, &QgsDualView::rebuildFullLayerCache );
280  rebuildFullLayerCache();
281  }
282 }
283 
284 void QgsDualView::initModels( QgsMapCanvas *mapCanvas, const QgsFeatureRequest &request, bool loadFeatures )
285 {
286  delete mFeatureListModel;
287  delete mFilterModel;
288  delete mMasterModel;
289 
290  mMasterModel = new QgsAttributeTableModel( mLayerCache, this );
291  mMasterModel->setRequest( request );
292  mMasterModel->setEditorContext( mEditorContext );
293  mMasterModel->setExtraColumns( 1 ); // Add one extra column which we can "abuse" as an action column
294 
295  connect( mMasterModel, &QgsAttributeTableModel::progress, this, &QgsDualView::progress );
296  connect( mMasterModel, &QgsAttributeTableModel::finished, this, &QgsDualView::finished );
297 
299 
300  if ( loadFeatures )
301  mMasterModel->loadLayer();
302 
303  mFilterModel = new QgsAttributeTableFilterModel( mapCanvas, mMasterModel, mMasterModel );
304 
306 
307  mFeatureListModel = new QgsFeatureListModel( mFilterModel, mFilterModel );
308 }
309 
310 void QgsDualView::on_mFeatureList_aboutToChangeEditSelection( bool &ok )
311 {
312  if ( mLayer->isEditable() && !mAttributeForm->save() )
313  ok = false;
314 }
315 
316 void QgsDualView::on_mFeatureList_currentEditSelectionChanged( const QgsFeature &feat )
317 {
318  if ( !mLayer->isEditable() || mAttributeForm->save() )
319  {
320  mAttributeForm->setFeature( feat );
322  }
323  else
324  {
325  // Couldn't save feature
326  }
327 }
328 
330 {
331  mFeatureList->setCurrentFeatureEdited( false );
332  mFeatureList->setEditSelection( fids );
333 }
334 
336 {
337  return mAttributeForm->save();
338 }
339 
341 {
342  mConditionalFormatWidget->setVisible( !mConditionalFormatWidget->isVisible() );
343  mConditionalFormatWidget->viewRules();
344 }
345 
347 {
348  if ( enabled )
350 
352 }
353 
354 void QgsDualView::toggleSearchMode( bool enabled )
355 {
356  if ( enabled )
357  {
359  mAttributeForm->setMode( QgsAttributeForm::SearchMode );
360  }
361  else
362  {
363  mAttributeForm->setMode( QgsAttributeForm::SingleEditMode );
364  }
365 }
366 
367 void QgsDualView::previewExpressionBuilder()
368 {
369  // Show expression builder
371 
372  QgsExpressionBuilderDialog dlg( mLayer, mFeatureList->displayExpression(), this, QStringLiteral( "generic" ), context );
373  dlg.setWindowTitle( tr( "Expression based preview" ) );
374  dlg.setExpressionText( mFeatureList->displayExpression() );
375 
376  if ( dlg.exec() == QDialog::Accepted )
377  {
378  mFeatureList->setDisplayExpression( dlg.expressionText() );
379  mFeatureListPreviewButton->setDefaultAction( mActionExpressionPreview );
380  mFeatureListPreviewButton->setPopupMode( QToolButton::MenuButtonPopup );
381  }
382 
383  mDisplayExpression = mFeatureList->displayExpression();
384 }
385 
386 void QgsDualView::previewColumnChanged( QObject *action )
387 {
388  QAction *previewAction = qobject_cast< QAction * >( action );
389 
390  if ( previewAction )
391  {
392  if ( !mFeatureList->setDisplayExpression( QStringLiteral( "COALESCE( \"%1\", '<NULL>' )" ).arg( previewAction->text() ) ) )
393  {
394  QMessageBox::warning( this,
395  tr( "Could not set preview column" ),
396  tr( "Could not set column '%1' as preview column.\nParser error:\n%2" )
397  .arg( previewAction->text(), mFeatureList->parserErrorString() )
398  );
399  }
400  else
401  {
402  mFeatureListPreviewButton->setDefaultAction( previewAction );
403  mFeatureListPreviewButton->setPopupMode( QToolButton::InstantPopup );
404  }
405  }
406 
407  mDisplayExpression = mFeatureList->displayExpression();
408 
409  Q_ASSERT( previewAction );
410 }
411 
413 {
414  return mMasterModel->rowCount();
415 }
416 
418 {
419  return mFilterModel->rowCount();
420 }
421 
423 {
424  QAction *action = qobject_cast<QAction *>( sender() );
425 
426  if ( action && action->data().isValid() && action->data().canConvert<QModelIndex>() )
427  {
428  QModelIndex index = action->data().toModelIndex();
429  QVariant var = masterModel()->data( index, Qt::DisplayRole );
430  QApplication::clipboard()->setText( var.toString() );
431  }
432 }
433 
434 void QgsDualView::viewWillShowContextMenu( QMenu *menu, const QModelIndex &atIndex )
435 {
436  if ( !menu )
437  {
438  return;
439  }
440 
441 
442  QModelIndex sourceIndex = mFilterModel->mapToSource( atIndex );
443 
444  QAction *copyContentAction = new QAction( tr( "Copy cell content" ), this );
445  copyContentAction->setData( QVariant::fromValue<QModelIndex>( sourceIndex ) );
446  menu->addAction( copyContentAction );
447  connect( copyContentAction, &QAction::triggered, this, &QgsDualView::copyCellContent );
448 
449  QgsVectorLayer *vl = mFilterModel->layer();
450  QgsMapCanvas *canvas = mFilterModel->mapCanvas();
451  if ( canvas && vl && vl->geometryType() != QgsWkbTypes::NullGeometry )
452  {
453  menu->addAction( tr( "Zoom to feature" ), this, SLOT( zoomToCurrentFeature() ) );
454  menu->addAction( tr( "Pan to feature" ), this, SLOT( panToCurrentFeature() ) );
455  }
456 
457  //add user-defined actions to context menu
458  QList<QgsAction> actions = mLayer->actions()->actions( QStringLiteral( "Field" ) );
459  if ( !actions.isEmpty() )
460  {
461  QAction *a = menu->addAction( tr( "Run layer action" ) );
462  a->setEnabled( false );
463 
464  Q_FOREACH ( const QgsAction &action, actions )
465  {
466  if ( !action.runable() )
467  continue;
468 
469  QgsAttributeTableAction *a = new QgsAttributeTableAction( action.name(), this, action.id(), sourceIndex );
470  menu->addAction( action.name(), a, SLOT( execute() ) );
471  }
472  }
473 
474  //add actions from QgsMapLayerActionRegistry to context menu
475  QList<QgsMapLayerAction *> registeredActions = QgsMapLayerActionRegistry::instance()->mapLayerActions( mLayer );
476  if ( !registeredActions.isEmpty() )
477  {
478  //add a separator between user defined and standard actions
479  menu->addSeparator();
480 
481  QList<QgsMapLayerAction *>::iterator actionIt;
482  for ( actionIt = registeredActions.begin(); actionIt != registeredActions.end(); ++actionIt )
483  {
484  QgsAttributeTableMapLayerAction *a = new QgsAttributeTableMapLayerAction( ( *actionIt )->text(), this, ( *actionIt ), sourceIndex );
485  menu->addAction( ( *actionIt )->text(), a, SLOT( execute() ) );
486  }
487  }
488 
489  menu->addSeparator();
490  QgsAttributeTableAction *a = new QgsAttributeTableAction( tr( "Open form" ), this, QString(), sourceIndex );
491  menu->addAction( tr( "Open form" ), a, SLOT( featureForm() ) );
492 }
493 
494 void QgsDualView::showViewHeaderMenu( QPoint point )
495 {
496  int col = mTableView->columnAt( point.x() );
497 
498  delete mHorizontalHeaderMenu;
499  mHorizontalHeaderMenu = new QMenu( this );
500 
501  QAction *hide = new QAction( tr( "&Hide column" ), mHorizontalHeaderMenu );
502  connect( hide, &QAction::triggered, this, &QgsDualView::hideColumn );
503  hide->setData( col );
504  mHorizontalHeaderMenu->addAction( hide );
505  QAction *setWidth = new QAction( tr( "&Set width..." ), mHorizontalHeaderMenu );
506  connect( setWidth, &QAction::triggered, this, &QgsDualView::resizeColumn );
507  setWidth->setData( col );
508  mHorizontalHeaderMenu->addAction( setWidth );
509  QAction *optimizeWidth = new QAction( tr( "&Autosize" ), mHorizontalHeaderMenu );
510  connect( optimizeWidth, &QAction::triggered, this, &QgsDualView::autosizeColumn );
511  optimizeWidth->setData( col );
512  mHorizontalHeaderMenu->addAction( optimizeWidth );
513 
514  mHorizontalHeaderMenu->addSeparator();
515  QAction *organize = new QAction( tr( "&Organize columns..." ), mHorizontalHeaderMenu );
516  connect( organize, &QAction::triggered, this, &QgsDualView::organizeColumns );
517  mHorizontalHeaderMenu->addAction( organize );
518  QAction *sort = new QAction( tr( "&Sort..." ), mHorizontalHeaderMenu );
519  connect( sort, &QAction::triggered, this, &QgsDualView::modifySort );
520  mHorizontalHeaderMenu->addAction( sort );
521 
522  mHorizontalHeaderMenu->popup( mTableView->horizontalHeader()->mapToGlobal( point ) );
523 }
524 
525 void QgsDualView::organizeColumns()
526 {
527  if ( !mLayer )
528  {
529  return;
530  }
531 
532  QgsOrganizeTableColumnsDialog dialog( mLayer, this );
533  if ( dialog.exec() == QDialog::Accepted )
534  {
535  QgsAttributeTableConfig config = dialog.config();
536  setAttributeTableConfig( config );
537  }
538 }
539 
540 void QgsDualView::tableColumnResized( int column, int width )
541 {
542  QgsAttributeTableConfig config = mConfig;
543  int sourceCol = config.mapVisibleColumnToIndex( column );
544  if ( sourceCol >= 0 )
545  {
546  config.setColumnWidth( sourceCol, width );
547  setAttributeTableConfig( config );
548  }
549 }
550 
551 void QgsDualView::hideColumn()
552 {
553  QAction *action = qobject_cast<QAction *>( sender() );
554  int col = action->data().toInt();
555  QgsAttributeTableConfig config = mConfig;
556  int sourceCol = mConfig.mapVisibleColumnToIndex( col );
557  if ( sourceCol >= 0 )
558  {
559  config.setColumnHidden( sourceCol, true );
560  setAttributeTableConfig( config );
561  }
562 }
563 
564 void QgsDualView::resizeColumn()
565 {
566  QAction *action = qobject_cast<QAction *>( sender() );
567  int col = action->data().toInt();
568  if ( col < 0 )
569  return;
570 
571  QgsAttributeTableConfig config = mConfig;
572  int sourceCol = config.mapVisibleColumnToIndex( col );
573  if ( sourceCol >= 0 )
574  {
575  bool ok = false;
576  int width = QInputDialog::getInt( this, tr( "Set column width" ), tr( "Enter column width" ),
577  mTableView->columnWidth( col ),
578  0, 1000, 10, &ok );
579  if ( ok )
580  {
581  config.setColumnWidth( sourceCol, width );
582  setAttributeTableConfig( config );
583  }
584  }
585 }
586 
587 void QgsDualView::autosizeColumn()
588 {
589  QAction *action = qobject_cast<QAction *>( sender() );
590  int col = action->data().toInt();
591  mTableView->resizeColumnToContents( col );
592 }
593 
594 void QgsDualView::modifySort()
595 {
596  if ( !mLayer )
597  return;
598 
599  QgsAttributeTableConfig config = mConfig;
600 
601  QDialog orderByDlg;
602  orderByDlg.setWindowTitle( tr( "Configure attribute table sort order" ) );
603  QDialogButtonBox *dialogButtonBox = new QDialogButtonBox( QDialogButtonBox::Ok | QDialogButtonBox::Cancel );
604  QGridLayout *layout = new QGridLayout();
605  connect( dialogButtonBox, &QDialogButtonBox::accepted, &orderByDlg, &QDialog::accept );
606  connect( dialogButtonBox, &QDialogButtonBox::rejected, &orderByDlg, &QDialog::reject );
607  orderByDlg.setLayout( layout );
608 
609  QGroupBox *sortingGroupBox = new QGroupBox();
610  sortingGroupBox->setTitle( tr( "Defined sort order in attribute table" ) );
611  sortingGroupBox->setCheckable( true );
612  sortingGroupBox->setChecked( !sortExpression().isEmpty() );
613  layout->addWidget( sortingGroupBox );
614  sortingGroupBox->setLayout( new QGridLayout() );
615 
616  QgsExpressionBuilderWidget *expressionBuilder = new QgsExpressionBuilderWidget();
618  expressionBuilder->setExpressionContext( context );
619  expressionBuilder->setLayer( mLayer );
620  expressionBuilder->loadFieldNames();
621  expressionBuilder->loadRecent( QStringLiteral( "generic" ) );
622  expressionBuilder->setExpressionText( sortExpression().isEmpty() ? mLayer->displayExpression() : sortExpression() );
623 
624  sortingGroupBox->layout()->addWidget( expressionBuilder );
625 
626  QCheckBox *cbxSortAscending = new QCheckBox( tr( "Sort ascending" ) );
627  cbxSortAscending->setChecked( config.sortOrder() == Qt::AscendingOrder );
628  sortingGroupBox->layout()->addWidget( cbxSortAscending );
629 
630  layout->addWidget( dialogButtonBox );
631  if ( orderByDlg.exec() )
632  {
633  Qt::SortOrder sortOrder = cbxSortAscending->isChecked() ? Qt::AscendingOrder : Qt::DescendingOrder;
634  if ( sortingGroupBox->isChecked() )
635  {
636  setSortExpression( expressionBuilder->expressionText(), sortOrder );
637  config.setSortExpression( expressionBuilder->expressionText() );
638  config.setSortOrder( sortOrder );
639  }
640  else
641  {
642  setSortExpression( QString(), sortOrder );
643  config.setSortExpression( QString() );
644  }
645 
646  setAttributeTableConfig( config );
647  }
648 }
649 
650 void QgsDualView::zoomToCurrentFeature()
651 {
652  QModelIndex currentIndex = mTableView->currentIndex();
653  if ( !currentIndex.isValid() )
654  {
655  return;
656  }
657 
658  QgsFeatureIds ids;
659  ids.insert( mFilterModel->rowToId( currentIndex ) );
660  QgsMapCanvas *canvas = mFilterModel->mapCanvas();
661  if ( canvas )
662  {
663  canvas->zoomToFeatureIds( mLayer, ids );
664  }
665 }
666 
667 void QgsDualView::panToCurrentFeature()
668 {
669  QModelIndex currentIndex = mTableView->currentIndex();
670  if ( !currentIndex.isValid() )
671  {
672  return;
673  }
674 
675  QgsFeatureIds ids;
676  ids.insert( mFilterModel->rowToId( currentIndex ) );
677  QgsMapCanvas *canvas = mFilterModel->mapCanvas();
678  if ( canvas )
679  {
680  canvas->panToFeatureIds( mLayer, ids );
681  }
682 }
683 
684 void QgsDualView::rebuildFullLayerCache()
685 {
686  connect( mLayerCache, &QgsVectorLayerCache::progress, this, &QgsDualView::progress, Qt::UniqueConnection );
687  connect( mLayerCache, &QgsVectorLayerCache::finished, this, &QgsDualView::finished, Qt::UniqueConnection );
688 
689  mLayerCache->setFullCache( true );
690 }
691 
692 void QgsDualView::previewExpressionChanged( const QString &expression )
693 {
694  mLayer->setDisplayExpression( expression );
695 }
696 
697 void QgsDualView::onSortColumnChanged()
698 {
700  cfg.setSortExpression( mFilterModel->sortExpression() );
701  cfg.setSortOrder( mFilterModel->sortOrder() );
703 }
704 
705 void QgsDualView::sortByPreviewExpression()
706 {
707  Qt::SortOrder sortOrder = Qt::AscendingOrder;
708  if ( mFeatureList->displayExpression() == sortExpression() )
709  {
710  sortOrder = mConfig.sortOrder() == Qt::AscendingOrder ? Qt::DescendingOrder : Qt::AscendingOrder;
711  }
712  setSortExpression( mFeatureList->displayExpression(), sortOrder );
713 }
714 
715 void QgsDualView::updateSelectedFeatures()
716 {
717  QgsFeatureRequest r = mMasterModel->request();
719  return; // already requested all features
720 
721  r.setFilterFids( masterModel()->layer()->selectedFeatureIds() );
722  mMasterModel->setRequest( r );
723  mMasterModel->loadLayer();
724  emit filterChanged();
725 }
726 
727 void QgsDualView::extentChanged()
728 {
729  QgsFeatureRequest r = mMasterModel->request();
730  if ( mMapCanvas && ( r.filterType() != QgsFeatureRequest::FilterNone || !r.filterRect().isNull() ) )
731  {
732  QgsRectangle rect = mMapCanvas->mapSettings().mapToLayerCoordinates( mLayer, mMapCanvas->extent() );
733  r.setFilterRect( rect );
734  mMasterModel->setRequest( r );
735  mMasterModel->loadLayer();
736  }
737  emit filterChanged();
738 }
739 
740 void QgsDualView::featureFormAttributeChanged()
741 {
742  mFeatureList->setCurrentFeatureEdited( true );
743 }
744 
746 {
747  mFilterModel->setFilteredFeatures( filteredFeatures );
748 }
749 
751 {
752  mMasterModel->setRequest( request );
753 }
754 
756 {
757  mTableView->setFeatureSelectionManager( featureSelectionManager );
758  mFeatureList->setFeatureSelectionManager( featureSelectionManager );
759 
760  if ( mFeatureSelectionManager && mFeatureSelectionManager->parent() == this )
761  delete mFeatureSelectionManager;
762 
763  mFeatureSelectionManager = featureSelectionManager;
764 }
765 
767 {
768  mLayer->setAttributeTableConfig( config );
769  mFilterModel->setAttributeTableConfig( config );
770  mTableView->setAttributeTableConfig( config );
771  mConfig = config;
772 }
773 
774 void QgsDualView::setSortExpression( const QString &sortExpression, Qt::SortOrder sortOrder )
775 {
776  if ( sortExpression.isNull() )
777  mFilterModel->sort( -1 );
778  else
779  mFilterModel->sort( sortExpression, sortOrder );
780 
781  mConfig.setSortExpression( sortExpression );
782  mConfig.setSortOrder( sortOrder );
783  setAttributeTableConfig( mConfig );
784 }
785 
787 {
788  return mFilterModel->sortExpression();
789 }
790 
791 void QgsDualView::progress( int i, bool &cancel )
792 {
793  if ( !mProgressDlg )
794  {
795  mProgressDlg = new QProgressDialog( tr( "Loading features..." ), tr( "Abort" ), 0, 0, this );
796  mProgressDlg->setWindowTitle( tr( "Attribute table" ) );
797  mProgressDlg->setWindowModality( Qt::WindowModal );
798  mProgressDlg->show();
799  }
800 
801  mProgressDlg->setLabelText( tr( "%1 features loaded." ).arg( i ) );
802  QCoreApplication::processEvents();
803 
804  cancel = mProgressDlg && mProgressDlg->wasCanceled();
805 }
806 
807 void QgsDualView::finished()
808 {
809  delete mProgressDlg;
810  mProgressDlg = nullptr;
811 }
812 
813 /*
814  * QgsAttributeTableAction
815  */
816 
818 {
819  mDualView->masterModel()->executeAction( mAction, mFieldIdx );
820 }
821 
823 {
824  QgsFeatureIds editedIds;
825  editedIds << mDualView->masterModel()->rowToId( mFieldIdx.row() );
826  mDualView->setCurrentEditSelection( editedIds );
827  mDualView->setView( QgsDualView::AttributeEditor );
828 }
829 
830 /*
831  * QgsAttributeTableMapLayerAction
832  */
833 
835 {
836  mDualView->masterModel()->executeMapLayerAction( mAction, mFieldIdx );
837 }
int lookupField(const QString &fieldName) const
Look up field&#39;s index from the field name.
Definition: qgsfields.cpp:289
void setRequest(const QgsFeatureRequest &request)
Set a request that will be used to fill this attribute table model.
QgsActionManager * actions()
Get all layer actions defined on this layer.
QgsFeatureId id
Definition: qgsfeature.h:140
QgsFeatureId rowToId(const QModelIndex &row)
Returns the feature id for a given model index.
QgsVectorLayer * layer() const
Returns the layer this filter acts on.
static unsigned index
void setFilterMode(QgsAttributeTableFilterModel::FilterMode filterMode)
Set the filter mode.
A rectangle specified with double values.
Definition: qgsrectangle.h:36
void setSortExpression(const QString &sortExpression)
Set the sort expression used for sorting.
virtual void loadLayer()
Loads the layer into the model Preferably to be called, before using this model as source for any oth...
void setAttributeTableConfig(const QgsAttributeTableConfig &config)
Set the attribute table configuration to control which fields are shown, in which order they are show...
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.
Definition: qgsdualview.cpp:72
void setExpressionText(const QString &text)
virtual Capabilities capabilities() const
Returns flags containing the supported capabilities.
QgsFeatureId idxToFid(const QModelIndex &index) const
QgsAttributeTableConfig config() const
Get the updated configuration.
void willShowContextMenu(QMenu *menu, const QModelIndex &atIndex)
Is emitted, in order to provide a hook to add additional* menu entries to the context menu...
const Flags & flags() const
QString name
Definition: qgsfield.h:53
void setSelectedOnTop(bool selectedOnTop)
Changes the sort order of the features.
QgsAttributeTableModel * masterModel() const
Returns the model which has the information about all features (not only filtered) ...
Definition: qgsdualview.h:159
void filterExpressionSet(const QString &expression, QgsAttributeForm::FilterType type)
Is emitted when a filter expression is set using the view.
void setSortOrder(Qt::SortOrder sortOrder)
Set the sort order.
void setFilterMode(FilterMode filterMode)
Set the filter mode the filter will use.
void toggleSearchMode(bool enabled)
Toggles whether search mode should be enabled in the form.
void setFeatureSelectionManager(QgsIFeatureSelectionManager *featureSelectionManager)
Set the feature selection model.
This class is a composition of two QSettings instances:
Definition: qgssettings.h:51
void invalidated()
The cache has been invalidated and cleared.
void openConditionalStyles()
Multi edit mode, for editing fields of multiple features at once.
QIcon iconForField(int fieldIdx) const
Returns an icon corresponding to a field index, based on the field&#39;s type and source.
Definition: qgsfields.cpp:247
QSet< QgsFeatureId > QgsFeatureIds
Definition: qgsfeature.h:358
QString sortExpression() const
Get the expression used for sorting the table and feature list.
This class contains context information for attribute editor widgets.
void setLayer(QgsVectorLayer *layer)
Sets layer in order to get the fields and values.
bool save()
Save all the values from the editors to the layer.
ViewMode
The view modes, in which this widget can present information.
Definition: qgsdualview.h:53
void loadRecent(const QString &collection="generic")
Loads the recent expressions from the given collection.
QgsPoint mapToLayerCoordinates(const QgsMapLayer *layer, QgsPoint point) const
transform point coordinates from output CRS to layer&#39;s CRS
FilterType filterType() const
Return the filter type which is currently set on this request.
QgsWkbTypes::GeometryType geometryType() const
Returns point, line or polygon.
virtual QModelIndex mapToSource(const QModelIndex &proxyIndex) const override
static QIcon getThemeIcon(const QString &name)
Helper to get a theme icon.
QList< QgsAction > actions(const QString &actionScope=QString()) const
Return a list of actions that are available in the given action scope.
void fieldConditionalStyleChanged(const QString &fieldName)
Handles updating the model when the conditional style for a field changes.
int filteredFeatureCount()
Returns the number of features which are currently visible, according to the filter restrictions...
void columnResized(int column, int width)
Emitted when a column in the view has been resized.
void setDisplayExpression(const QString &displayExpression)
Set the preview expression, used to create a human readable preview string.
The feature class encapsulates a single feature including its id, geometry and a list of field/values...
Definition: qgsfeature.h:136
A model backed by a QgsVectorLayerCache which is able to provide feature/attribute information to a Q...
Show only visible features (depends on the map canvas)
void setCurrentEditSelection(const QgsFeatureIds &fids)
Set the current edit selection in the AttributeEditor mode.
int mapVisibleColumnToIndex(int visibleColumn) const
Maps a visible column index to its original column index.
virtual bool isEditable() const override
Returns true if the provider is in editing mode.
static QgsEditorWidgetRegistry * instance()
This class is a singleton and has therefore to be accessed with this method instead of a constructor...
const QgsRectangle & filterRect() const
Get the rectangle from which features will be taken.
Map canvas is a class for displaying all GIS data types on a canvas.
Definition: qgsmapcanvas.h:72
void setView(ViewMode view)
Change the current view mode.
virtual void setFilteredFeatures(const QgsFeatureIds &ids)
Specify a list of features, which the filter will accept.
QgsFields fields() const
Returns the list of fields of this layer.
virtual QVariant data(const QModelIndex &index, int role) const override
Returns data on the given index.
void setAttributeTableConfig(const QgsAttributeTableConfig &config)
Set the attribute table config which should be used to control the appearance of the attribute table...
QgsDualView(QWidget *parent=nullptr)
Constructor.
Definition: qgsdualview.cpp:41
Show a list of the features, where one can be chosen and the according attribute dialog will be prese...
Definition: qgsdualview.h:66
virtual QModelIndex index(int row, int column, const QModelIndex &parent=QModelIndex()) const override
void setMode(Mode mode)
Sets the current mode of the form.
void setColumnWidth(int column, int width)
Sets the width of a column.
void copyCellContent() const
Copy the content of the selected cell in the clipboard.
void setSortExpression(const QString &sortExpression, Qt::SortOrder sortOrder=Qt::AscendingOrder)
Set the expression used for sorting the table and feature list.
FilterMode filterMode()
The current filterModel.
void setExtraColumns(int extraColumns)
Empty extra columns to announce from this model.
void setFeature(const QgsFeature &feature)
Update all editors to correspond to a different feature.
FilterMode
The filter mode defines how the rows should be filtered.
Dialog for organising (hiding and reordering) columns in the attributes table.
Form values are used for searching/filtering the layer.
void setEditorContext(const QgsAttributeEditorContext &context)
Sets the context in which this table is shown.
QgsFeatureRequest & disableFilter()
Disables filter conditions.
void filterExpressionSet(const QString &expression, QgsAttributeForm::FilterType type)
Is emitted when a filter expression is set using the form.
void selectionChanged(const QgsFeatureIds &selected, const QgsFeatureIds &deselected, const bool clearAndSelect)
This signal is emitted when selection was changed.
ViewMode view() const
Returns the current view mode.
Utility class that encapsulates an action based on vector attributes.
Definition: qgsaction.h:34
Expression contexts are used to encapsulate the parameters around which a QgsExpression should be eva...
void progress(int i, bool &cancel)
When filling the cache, this signal gets emitted periodically to notify about the progress and to be ...
QgsRectangle extent() const
Returns the current zoom extent of the map canvas.
void panToFeatureIds(QgsVectorLayer *layer, const QgsFeatureIds &ids)
Centers canvas extent to feature ids.
This class wraps a request for features to a vector layer (or directly its vector data provider)...
void modeChanged(QgsAttributeForm::Mode mode)
Emitted when the form changes mode.
bool runable() const
Checks if the action is runable on the current platform.
Definition: qgsaction.cpp:29
QgsFeatureIds filteredFeatures()
Get a list of currently visible feature ids.
Definition: qgsdualview.h:152
void refreshFeature()
reload current feature
void displayExpressionChanged(const QString &expression)
Is emitted, whenever the display expression is successfully changed.
void filterChanged()
Is emitted, whenever the filter changes.
QString name() const
The name of the action. This may be a longer description.
Definition: qgsaction.h:93
void loadFieldNames()
Loads all the field names from the layer.
Show only features which have unsaved changes.
const QgsFeatureRequest & request() const
Get the the feature request.
static QList< QgsExpressionContextScope * > globalProjectLayerScopes(const QgsMapLayer *layer)
Creates a list of three scopes: global, layer&#39;s project and layer.
Encapsulate a field in an attribute table or data source.
Definition: qgsfield.h:45
Fast access to features using their ID.
QString displayExpression
void setExpressionContext(const QgsExpressionContext &context)
Sets the expression context for the widget.
QString sortExpression() const
The expression which is used to sort the attribute table.
This class caches features of a given QgsVectorLayer.
const QgsAttributeEditorContext * parentContext() const
bool saveEditChanges()
saveEditChanges
Show only features whose ids are on the filter list. {.
void progress(int i, bool &cancel)
const QgsMapSettings & mapSettings() const
Get access to properties used for map rendering.
void setRequest(const QgsFeatureRequest &request)
Set the request.
QgsAttributeTableFilterModel::FilterMode filterMode()
Get the filter mode.
Definition: qgsdualview.h:116
void modelChanged()
Model has been changed.
A reusable widget that can be used to build a expression string.
QgsAttributeTableConfig attributeTableConfig() const
Get the attribute table configuration object.
void setSelectedOnTop(bool selectedOnTop)
Toggle the selectedOnTop flag.
QgsSignalBlocker< Object > whileBlocking(Object *object)
Temporarily blocks signals from a QObject while calling a single method from the object.
Definition: qgis.h:178
void zoomToFeatureIds(QgsVectorLayer *layer, const QgsFeatureIds &ids)
Set canvas extent to the bounding box of a set of features.
No filter is applied.
static QgsMapLayerActionRegistry * instance()
Returns the instance pointer, creating the object on the first call.
QgsFeatureRequest & setFilterFids(const QgsFeatureIds &fids)
Set feature IDs that should be fetched.
void attributeChanged(const QString &attribute, const QVariant &value)
Notifies about changes of attributes.
QgsEditorWidgetSetup findBest(const QgsVectorLayer *vl, const QString &fieldName) const
Find the best editor widget and its configuration for a given field.
virtual int rowCount(const QModelIndex &parent=QModelIndex()) const override
Returns the number of rows.
void setCacheGeometry(bool cacheGeometry)
Enable or disable the caching of geometries.
void setFullCache(bool fullCache)
This enables or disables full caching.
virtual void sort(int column, Qt::SortOrder order=Qt::AscendingOrder) override
Sort by the given column using the given order.
void setColumnHidden(int column, bool hidden)
Sets whether the specified column should be hidden.
QList< QgsField > toList() const
Utility function to return a list of QgsField instances.
Definition: qgsfields.cpp:184
bool isNull() const
test if the rectangle is null (all coordinates zero or after call to setMinimal()).
QgsMapCanvas * mapCanvas() const
Returns the map canvas.
void displayExpressionChanged(const QString &expression)
Is emitted, whenever the display expression is successfully changed.
QVariant value(const QString &key, const QVariant &defaultValue=QVariant(), const Section section=Section::NoSection) const
Returns the value for setting key.
QList< QgsMapLayerAction * > mapLayerActions(QgsMapLayer *layer, QgsMapLayerAction::Targets targets=QgsMapLayerAction::AllActions)
Returns the map layer actions which can run on the specified layer.
QString expressionText()
Gets the expression string that has been set in the expression area.
void formModeChanged(QgsAttributeForm::Mode mode)
Emitted when the form changes mode.
void setExpressionText(const QString &expression)
Sets the expression string for the widget.
QUuid id() const
Returns a unique id for this action.
Definition: qgsaction.h:103
QgsVectorDataProvider * dataProvider()
Returns the data provider.
Single edit mode, for editing a single feature.
void setFilteredFeatures(const QgsFeatureIds &filteredFeatures)
Set a list of currently visible features.
This is a container for configuration of the attribute table.
Qt::SortOrder sortOrder() const
Get the sort order.
A QScrollArea subclass with improved scrolling behavior.
Definition: qgsscrollarea.h:40
Geometry is not required. It may still be returned if e.g. required for a filter condition.
void setMultiEditEnabled(bool enabled)
Sets whether multi edit mode is enabled.
Is an interface class to abstract feature selection handling.
Represents a vector layer which manages a vector based data sets.
void sortColumnChanged(int column, Qt::SortOrder order)
Is emitted whenever the sort column is changed.
void finished()
When filling the cache, this signal gets emitted once the cache is fully initialized.
void setAttributeTableConfig(const QgsAttributeTableConfig &attributeTableConfig)
Set the attribute table configuration object.
void extentsChanged()
Emitted when the extents of the map change.
A generic dialog for building expression strings.
QgsFeatureRequest & setFilterRect(const QgsRectangle &rect)
Set rectangle from which features will be taken.
void columnBoxInit()
Initializes widgets which depend on the attributes of this layer.
int featureCount()
Returns the number of features on the layer.
QgsFeatureRequest & setFlags(QgsFeatureRequest::Flags flags)
Set flags that affect how features will be fetched.
void rulesUpdated(const QString &fieldName)
Emitted when the conditional styling rules are updated.