QGIS API Documentation  2.99.0-Master (b698612)
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 #include "qgsgui.h"
33 
34 #include <QClipboard>
35 #include <QDialog>
36 #include <QMenu>
37 #include <QMessageBox>
38 #include <QProgressDialog>
39 #include <QGroupBox>
40 #include <QInputDialog>
41 
42 QgsDualView::QgsDualView( QWidget *parent )
43  : QStackedWidget( parent )
44  , mEditorContext()
45  , mMasterModel( nullptr )
46  , mFilterModel( nullptr )
47  , mFeatureListModel( nullptr )
48  , mAttributeForm( nullptr )
49  , mHorizontalHeaderMenu( nullptr )
50  , mLayerCache( nullptr )
51  , mProgressDlg( nullptr )
52  , mFeatureSelectionManager( nullptr )
53  , mAttributeEditorScrollArea( nullptr )
54 {
55  setupUi( this );
56 
57  mConditionalFormatWidget->hide();
58 
59  mPreviewActionMapper = new QSignalMapper( this );
60 
61  mPreviewColumnsMenu = new QMenu( this );
62  mActionPreviewColumnsMenu->setMenu( mPreviewColumnsMenu );
63 
64  // Set preview icon
65  mActionExpressionPreview->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mIconExpressionPreview.svg" ) ) );
66 
67  // Connect layer list preview signals
68  connect( mActionExpressionPreview, &QAction::triggered, this, &QgsDualView::previewExpressionBuilder );
69  connect( mPreviewActionMapper, static_cast < void ( QSignalMapper::* )( QObject * ) > ( &QSignalMapper::mapped ), this, &QgsDualView::previewColumnChanged );
70  connect( mFeatureList, &QgsFeatureListView::displayExpressionChanged, this, &QgsDualView::previewExpressionChanged );
71 }
72 
73 void QgsDualView::init( QgsVectorLayer *layer, QgsMapCanvas *mapCanvas, const QgsFeatureRequest &request, const QgsAttributeEditorContext &context, bool loadFeatures )
74 {
75  mMapCanvas = mapCanvas;
76 
77  if ( !layer )
78  return;
79 
80  mLayer = layer;
81 
82  mEditorContext = context;
83 
84  connect( mTableView, &QgsAttributeTableView::willShowContextMenu, this, &QgsDualView::viewWillShowContextMenu );
85  mTableView->horizontalHeader()->setContextMenuPolicy( Qt::CustomContextMenu );
86  connect( mTableView->horizontalHeader(), &QHeaderView::customContextMenuRequested, this, &QgsDualView::showViewHeaderMenu );
87  connect( mTableView, &QgsAttributeTableView::columnResized, this, &QgsDualView::tableColumnResized );
88 
89  initLayerCache( !( request.flags() & QgsFeatureRequest::NoGeometry ) || !request.filterRect().isNull() );
90  initModels( mapCanvas, request, loadFeatures );
91 
92  mConditionalFormatWidget->setLayer( mLayer );
93 
94  mTableView->setModel( mFilterModel );
95  mFeatureList->setModel( mFeatureListModel );
96  delete mAttributeForm;
97  mAttributeForm = new QgsAttributeForm( mLayer, QgsFeature(), mEditorContext );
98  if ( !context.parentContext() )
99  {
100  mAttributeEditorScrollArea = new QgsScrollArea();
101  mAttributeEditorScrollArea->setWidgetResizable( true );
102  mAttributeEditor->layout()->addWidget( mAttributeEditorScrollArea );
103  mAttributeEditorScrollArea->setWidget( mAttributeForm );
104  }
105  else
106  {
107  mAttributeEditor->layout()->addWidget( mAttributeForm );
108  }
109 
110  connect( mAttributeForm, &QgsAttributeForm::attributeChanged, this, &QgsDualView::featureFormAttributeChanged );
111  connect( mAttributeForm, &QgsAttributeForm::modeChanged, this, &QgsDualView::formModeChanged );
112  connect( mMasterModel, &QgsAttributeTableModel::modelChanged, mAttributeForm, &QgsAttributeForm::refreshFeature );
114  connect( mFilterModel, &QgsAttributeTableFilterModel::sortColumnChanged, this, &QgsDualView::onSortColumnChanged );
115 
116  if ( mFeatureListPreviewButton->defaultAction() )
117  mFeatureList->setDisplayExpression( mDisplayExpression );
118  else
119  columnBoxInit();
120 
121  // This slows down load of the attribute table heaps and uses loads of memory.
122  //mTableView->resizeColumnsToContents();
123 
124  mFeatureList->setEditSelection( QgsFeatureIds() << mFeatureListModel->idxToFid( mFeatureListModel->index( 0, 0 ) ) );
125 }
126 
128 {
129  // load fields
130  QList<QgsField> fields = mLayer->fields().toList();
131 
132  QString defaultField;
133 
134  // default expression: saved value
135  QString displayExpression = mLayer->displayExpression();
136 
137  if ( displayExpression.isEmpty() )
138  {
139  // ... there isn't really much to display
140  displayExpression = QStringLiteral( "'[Please define preview text]'" );
141  }
142 
143  mFeatureListPreviewButton->addAction( mActionExpressionPreview );
144  mFeatureListPreviewButton->addAction( mActionPreviewColumnsMenu );
145 
146  Q_FOREACH ( const QgsField &field, fields )
147  {
148  int fieldIndex = mLayer->fields().lookupField( field.name() );
149  if ( fieldIndex == -1 )
150  continue;
151 
152  if ( QgsGui::editorWidgetRegistry()->findBest( mLayer, field.name() ).type() != QLatin1String( "Hidden" ) )
153  {
154  QIcon icon = mLayer->fields().iconForField( fieldIndex );
155  QString text = field.name();
156 
157  // Generate action for the preview popup button of the feature list
158  QAction *previewAction = new QAction( icon, text, mFeatureListPreviewButton );
159  mPreviewActionMapper->setMapping( previewAction, previewAction );
160  connect( previewAction, &QAction::triggered, this, [previewAction, this]( bool ) { this->mPreviewActionMapper->map( previewAction ); }
161  );
162  mPreviewColumnsMenu->addAction( previewAction );
163 
164  if ( text == defaultField )
165  {
166  mFeatureListPreviewButton->setDefaultAction( previewAction );
167  }
168  }
169  }
170 
171  // If there is no single field found as preview
172  if ( !mFeatureListPreviewButton->defaultAction() )
173  {
174  mFeatureList->setDisplayExpression( displayExpression );
175  mFeatureListPreviewButton->setDefaultAction( mActionExpressionPreview );
176  mDisplayExpression = mFeatureList->displayExpression();
177  }
178  else
179  {
180  mFeatureListPreviewButton->defaultAction()->trigger();
181  }
182 
183  QAction *sortByPreviewExpression = new QAction( QgsApplication::getThemeIcon( QStringLiteral( "sort.svg" ) ), tr( "Sort by preview expression" ), this );
184  connect( sortByPreviewExpression, &QAction::triggered, this, &QgsDualView::sortByPreviewExpression );
185  mFeatureListPreviewButton->addAction( sortByPreviewExpression );
186 }
187 
189 {
190  setCurrentIndex( view );
191 }
192 
194 {
195  return static_cast< QgsDualView::ViewMode >( currentIndex() );
196 }
197 
199 {
200  // cleanup any existing connections
201  switch ( mFilterModel->filterMode() )
202  {
204  disconnect( mMapCanvas, &QgsMapCanvas::extentsChanged, this, &QgsDualView::extentChanged );
205  break;
206 
210  break;
211 
213  disconnect( masterModel()->layer(), &QgsVectorLayer::selectionChanged, this, &QgsDualView::updateSelectedFeatures );
214  break;
215  }
216 
217  QgsFeatureRequest r = mMasterModel->request();
218  bool needsGeometry = filterMode == QgsAttributeTableFilterModel::ShowVisible;
219 
220  bool requiresTableReload = ( r.filterType() != QgsFeatureRequest::FilterNone || !r.filterRect().isNull() ) // previous request was subset
221  || ( needsGeometry && r.flags() & QgsFeatureRequest::NoGeometry ) // no geometry for last request
222  || ( mMasterModel->rowCount() == 0 ); // no features
223 
224  if ( !needsGeometry )
226  else
230  r.disableFilter();
231 
232  // setup new connections and filter request parameters
233  switch ( filterMode )
234  {
236  connect( mMapCanvas, &QgsMapCanvas::extentsChanged, this, &QgsDualView::extentChanged );
237  if ( mMapCanvas )
238  {
239  QgsRectangle rect = mMapCanvas->mapSettings().mapToLayerCoordinates( mLayer, mMapCanvas->extent() );
240  r.setFilterRect( rect );
241  }
242  break;
243 
247  break;
248 
250  connect( masterModel()->layer(), &QgsVectorLayer::selectionChanged, this, &QgsDualView::updateSelectedFeatures );
251  r.setFilterFids( masterModel()->layer()->selectedFeatureIds() );
252  break;
253  }
254 
255  if ( requiresTableReload )
256  {
257  mMasterModel->setRequest( r );
258  whileBlocking( mLayerCache )->setCacheGeometry( needsGeometry );
259  mMasterModel->loadLayer();
260  }
261 
262  //update filter model
263  mFilterModel->setFilterMode( filterMode );
264  emit filterChanged();
265 }
266 
267 void QgsDualView::setSelectedOnTop( bool selectedOnTop )
268 {
269  mFilterModel->setSelectedOnTop( selectedOnTop );
270 }
271 
272 void QgsDualView::initLayerCache( bool cacheGeometry )
273 {
274  // Initialize the cache
275  QgsSettings settings;
276  int cacheSize = settings.value( QStringLiteral( "qgis/attributeTableRowCache" ), "10000" ).toInt();
277  mLayerCache = new QgsVectorLayerCache( mLayer, cacheSize, this );
278  mLayerCache->setCacheGeometry( cacheGeometry );
279  if ( 0 == cacheSize || 0 == ( QgsVectorDataProvider::SelectAtId & mLayer->dataProvider()->capabilities() ) )
280  {
281  connect( mLayerCache, &QgsVectorLayerCache::invalidated, this, &QgsDualView::rebuildFullLayerCache );
282  rebuildFullLayerCache();
283  }
284 }
285 
286 void QgsDualView::initModels( QgsMapCanvas *mapCanvas, const QgsFeatureRequest &request, bool loadFeatures )
287 {
288  delete mFeatureListModel;
289  delete mFilterModel;
290  delete mMasterModel;
291 
292  mMasterModel = new QgsAttributeTableModel( mLayerCache, this );
293  mMasterModel->setRequest( request );
294  mMasterModel->setEditorContext( mEditorContext );
295  mMasterModel->setExtraColumns( 1 ); // Add one extra column which we can "abuse" as an action column
296 
297  connect( mMasterModel, &QgsAttributeTableModel::progress, this, &QgsDualView::progress );
298  connect( mMasterModel, &QgsAttributeTableModel::finished, this, &QgsDualView::finished );
299 
301 
302  if ( loadFeatures )
303  mMasterModel->loadLayer();
304 
305  mFilterModel = new QgsAttributeTableFilterModel( mapCanvas, mMasterModel, mMasterModel );
306 
307  // The following connections to invalidate() are necessary to keep the filter model in sync
308  // see regression https://issues.qgis.org/issues/15974
309  connect( mMasterModel, &QgsAttributeTableModel::rowsRemoved, mFilterModel, &QgsAttributeTableFilterModel::invalidate );
310  connect( mMasterModel, &QgsAttributeTableModel::rowsInserted, mFilterModel, &QgsAttributeTableFilterModel::invalidate );
311 
313 
314  mFeatureListModel = new QgsFeatureListModel( mFilterModel, mFilterModel );
315 }
316 
317 void QgsDualView::on_mFeatureList_aboutToChangeEditSelection( bool &ok )
318 {
319  if ( mLayer->isEditable() && !mAttributeForm->save() )
320  ok = false;
321 }
322 
323 void QgsDualView::on_mFeatureList_currentEditSelectionChanged( const QgsFeature &feat )
324 {
325  if ( !mLayer->isEditable() || mAttributeForm->save() )
326  {
327  mAttributeForm->setFeature( feat );
329  }
330  else
331  {
332  // Couldn't save feature
333  }
334 }
335 
337 {
338  mFeatureList->setCurrentFeatureEdited( false );
339  mFeatureList->setEditSelection( fids );
340 }
341 
343 {
344  return mAttributeForm->save();
345 }
346 
348 {
349  mConditionalFormatWidget->setVisible( !mConditionalFormatWidget->isVisible() );
350  mConditionalFormatWidget->viewRules();
351 }
352 
354 {
355  if ( enabled )
357 
359 }
360 
361 void QgsDualView::toggleSearchMode( bool enabled )
362 {
363  if ( enabled )
364  {
366  mAttributeForm->setMode( QgsAttributeForm::SearchMode );
367  }
368  else
369  {
370  mAttributeForm->setMode( QgsAttributeForm::SingleEditMode );
371  }
372 }
373 
374 void QgsDualView::previewExpressionBuilder()
375 {
376  // Show expression builder
378 
379  QgsExpressionBuilderDialog dlg( mLayer, mFeatureList->displayExpression(), this, QStringLiteral( "generic" ), context );
380  dlg.setWindowTitle( tr( "Expression based preview" ) );
381  dlg.setExpressionText( mFeatureList->displayExpression() );
382 
383  if ( dlg.exec() == QDialog::Accepted )
384  {
385  mFeatureList->setDisplayExpression( dlg.expressionText() );
386  mFeatureListPreviewButton->setDefaultAction( mActionExpressionPreview );
387  mFeatureListPreviewButton->setPopupMode( QToolButton::MenuButtonPopup );
388  }
389 
390  mDisplayExpression = mFeatureList->displayExpression();
391 }
392 
393 void QgsDualView::previewColumnChanged( QObject *action )
394 {
395  QAction *previewAction = qobject_cast< QAction * >( action );
396 
397  if ( previewAction )
398  {
399  if ( !mFeatureList->setDisplayExpression( QStringLiteral( "COALESCE( \"%1\", '<NULL>' )" ).arg( previewAction->text() ) ) )
400  {
401  QMessageBox::warning( this,
402  tr( "Could not set preview column" ),
403  tr( "Could not set column '%1' as preview column.\nParser error:\n%2" )
404  .arg( previewAction->text(), mFeatureList->parserErrorString() )
405  );
406  }
407  else
408  {
409  mFeatureListPreviewButton->setDefaultAction( previewAction );
410  mFeatureListPreviewButton->setPopupMode( QToolButton::InstantPopup );
411  }
412  }
413 
414  mDisplayExpression = mFeatureList->displayExpression();
415 
416  Q_ASSERT( previewAction );
417 }
418 
420 {
421  return mMasterModel->rowCount();
422 }
423 
425 {
426  return mFilterModel->rowCount();
427 }
428 
430 {
431  QAction *action = qobject_cast<QAction *>( sender() );
432 
433  if ( action && action->data().isValid() && action->data().canConvert<QModelIndex>() )
434  {
435  QModelIndex index = action->data().toModelIndex();
436  QVariant var = masterModel()->data( index, Qt::DisplayRole );
437  QApplication::clipboard()->setText( var.toString() );
438  }
439 }
440 
441 void QgsDualView::viewWillShowContextMenu( QMenu *menu, const QModelIndex &atIndex )
442 {
443  if ( !menu )
444  {
445  return;
446  }
447 
448 
449  QModelIndex sourceIndex = mFilterModel->mapToSource( atIndex );
450 
451  QAction *copyContentAction = new QAction( tr( "Copy cell content" ), this );
452  copyContentAction->setData( QVariant::fromValue<QModelIndex>( sourceIndex ) );
453  menu->addAction( copyContentAction );
454  connect( copyContentAction, &QAction::triggered, this, &QgsDualView::copyCellContent );
455 
456  QgsVectorLayer *vl = mFilterModel->layer();
457  QgsMapCanvas *canvas = mFilterModel->mapCanvas();
458  if ( canvas && vl && vl->geometryType() != QgsWkbTypes::NullGeometry )
459  {
460  menu->addAction( tr( "Zoom to feature" ), this, SLOT( zoomToCurrentFeature() ) );
461  menu->addAction( tr( "Pan to feature" ), this, SLOT( panToCurrentFeature() ) );
462  }
463 
464  //add user-defined actions to context menu
465  QList<QgsAction> actions = mLayer->actions()->actions( QStringLiteral( "Field" ) );
466  if ( !actions.isEmpty() )
467  {
468  QAction *a = menu->addAction( tr( "Run layer action" ) );
469  a->setEnabled( false );
470 
471  Q_FOREACH ( const QgsAction &action, actions )
472  {
473  if ( !action.runable() )
474  continue;
475 
476  QgsAttributeTableAction *a = new QgsAttributeTableAction( action.name(), this, action.id(), sourceIndex );
477  menu->addAction( action.name(), a, SLOT( execute() ) );
478  }
479  }
480 
481  //add actions from QgsMapLayerActionRegistry to context menu
482  QList<QgsMapLayerAction *> registeredActions = QgsGui::mapLayerActionRegistry()->mapLayerActions( mLayer );
483  if ( !registeredActions.isEmpty() )
484  {
485  //add a separator between user defined and standard actions
486  menu->addSeparator();
487 
488  QList<QgsMapLayerAction *>::iterator actionIt;
489  for ( actionIt = registeredActions.begin(); actionIt != registeredActions.end(); ++actionIt )
490  {
491  QgsAttributeTableMapLayerAction *a = new QgsAttributeTableMapLayerAction( ( *actionIt )->text(), this, ( *actionIt ), sourceIndex );
492  menu->addAction( ( *actionIt )->text(), a, SLOT( execute() ) );
493  }
494  }
495 
496  menu->addSeparator();
497  QgsAttributeTableAction *a = new QgsAttributeTableAction( tr( "Open form" ), this, QString(), sourceIndex );
498  menu->addAction( tr( "Open form" ), a, SLOT( featureForm() ) );
499 }
500 
501 void QgsDualView::showViewHeaderMenu( QPoint point )
502 {
503  int col = mTableView->columnAt( point.x() );
504 
505  delete mHorizontalHeaderMenu;
506  mHorizontalHeaderMenu = new QMenu( this );
507 
508  QAction *hide = new QAction( tr( "&Hide column" ), mHorizontalHeaderMenu );
509  connect( hide, &QAction::triggered, this, &QgsDualView::hideColumn );
510  hide->setData( col );
511  mHorizontalHeaderMenu->addAction( hide );
512  QAction *setWidth = new QAction( tr( "&Set width..." ), mHorizontalHeaderMenu );
513  connect( setWidth, &QAction::triggered, this, &QgsDualView::resizeColumn );
514  setWidth->setData( col );
515  mHorizontalHeaderMenu->addAction( setWidth );
516  QAction *optimizeWidth = new QAction( tr( "&Autosize" ), mHorizontalHeaderMenu );
517  connect( optimizeWidth, &QAction::triggered, this, &QgsDualView::autosizeColumn );
518  optimizeWidth->setData( col );
519  mHorizontalHeaderMenu->addAction( optimizeWidth );
520 
521  mHorizontalHeaderMenu->addSeparator();
522  QAction *organize = new QAction( tr( "&Organize columns..." ), mHorizontalHeaderMenu );
523  connect( organize, &QAction::triggered, this, &QgsDualView::organizeColumns );
524  mHorizontalHeaderMenu->addAction( organize );
525  QAction *sort = new QAction( tr( "&Sort..." ), mHorizontalHeaderMenu );
526  connect( sort, &QAction::triggered, this, &QgsDualView::modifySort );
527  mHorizontalHeaderMenu->addAction( sort );
528 
529  mHorizontalHeaderMenu->popup( mTableView->horizontalHeader()->mapToGlobal( point ) );
530 }
531 
532 void QgsDualView::organizeColumns()
533 {
534  if ( !mLayer )
535  {
536  return;
537  }
538 
539  QgsOrganizeTableColumnsDialog dialog( mLayer, this );
540  if ( dialog.exec() == QDialog::Accepted )
541  {
542  QgsAttributeTableConfig config = dialog.config();
543  setAttributeTableConfig( config );
544  }
545 }
546 
547 void QgsDualView::tableColumnResized( int column, int width )
548 {
549  QgsAttributeTableConfig config = mConfig;
550  int sourceCol = config.mapVisibleColumnToIndex( column );
551  if ( sourceCol >= 0 )
552  {
553  config.setColumnWidth( sourceCol, width );
554  setAttributeTableConfig( config );
555  }
556 }
557 
558 void QgsDualView::hideColumn()
559 {
560  QAction *action = qobject_cast<QAction *>( sender() );
561  int col = action->data().toInt();
562  QgsAttributeTableConfig config = mConfig;
563  int sourceCol = mConfig.mapVisibleColumnToIndex( col );
564  if ( sourceCol >= 0 )
565  {
566  config.setColumnHidden( sourceCol, true );
567  setAttributeTableConfig( config );
568  }
569 }
570 
571 void QgsDualView::resizeColumn()
572 {
573  QAction *action = qobject_cast<QAction *>( sender() );
574  int col = action->data().toInt();
575  if ( col < 0 )
576  return;
577 
578  QgsAttributeTableConfig config = mConfig;
579  int sourceCol = config.mapVisibleColumnToIndex( col );
580  if ( sourceCol >= 0 )
581  {
582  bool ok = false;
583  int width = QInputDialog::getInt( this, tr( "Set column width" ), tr( "Enter column width" ),
584  mTableView->columnWidth( col ),
585  0, 1000, 10, &ok );
586  if ( ok )
587  {
588  config.setColumnWidth( sourceCol, width );
589  setAttributeTableConfig( config );
590  }
591  }
592 }
593 
594 void QgsDualView::autosizeColumn()
595 {
596  QAction *action = qobject_cast<QAction *>( sender() );
597  int col = action->data().toInt();
598  mTableView->resizeColumnToContents( col );
599 }
600 
601 void QgsDualView::modifySort()
602 {
603  if ( !mLayer )
604  return;
605 
606  QgsAttributeTableConfig config = mConfig;
607 
608  QDialog orderByDlg;
609  orderByDlg.setWindowTitle( tr( "Configure attribute table sort order" ) );
610  QDialogButtonBox *dialogButtonBox = new QDialogButtonBox( QDialogButtonBox::Ok | QDialogButtonBox::Cancel );
611  QGridLayout *layout = new QGridLayout();
612  connect( dialogButtonBox, &QDialogButtonBox::accepted, &orderByDlg, &QDialog::accept );
613  connect( dialogButtonBox, &QDialogButtonBox::rejected, &orderByDlg, &QDialog::reject );
614  orderByDlg.setLayout( layout );
615 
616  QGroupBox *sortingGroupBox = new QGroupBox();
617  sortingGroupBox->setTitle( tr( "Defined sort order in attribute table" ) );
618  sortingGroupBox->setCheckable( true );
619  sortingGroupBox->setChecked( !sortExpression().isEmpty() );
620  layout->addWidget( sortingGroupBox );
621  sortingGroupBox->setLayout( new QGridLayout() );
622 
623  QgsExpressionBuilderWidget *expressionBuilder = new QgsExpressionBuilderWidget();
625  expressionBuilder->setExpressionContext( context );
626  expressionBuilder->setLayer( mLayer );
627  expressionBuilder->loadFieldNames();
628  expressionBuilder->loadRecent( QStringLiteral( "generic" ) );
629  expressionBuilder->setExpressionText( sortExpression().isEmpty() ? mLayer->displayExpression() : sortExpression() );
630 
631  sortingGroupBox->layout()->addWidget( expressionBuilder );
632 
633  QCheckBox *cbxSortAscending = new QCheckBox( tr( "Sort ascending" ) );
634  cbxSortAscending->setChecked( config.sortOrder() == Qt::AscendingOrder );
635  sortingGroupBox->layout()->addWidget( cbxSortAscending );
636 
637  layout->addWidget( dialogButtonBox );
638  if ( orderByDlg.exec() )
639  {
640  Qt::SortOrder sortOrder = cbxSortAscending->isChecked() ? Qt::AscendingOrder : Qt::DescendingOrder;
641  if ( sortingGroupBox->isChecked() )
642  {
643  setSortExpression( expressionBuilder->expressionText(), sortOrder );
644  config.setSortExpression( expressionBuilder->expressionText() );
645  config.setSortOrder( sortOrder );
646  }
647  else
648  {
649  setSortExpression( QString(), sortOrder );
650  config.setSortExpression( QString() );
651  }
652 
653  setAttributeTableConfig( config );
654  }
655 }
656 
657 void QgsDualView::zoomToCurrentFeature()
658 {
659  QModelIndex currentIndex = mTableView->currentIndex();
660  if ( !currentIndex.isValid() )
661  {
662  return;
663  }
664 
665  QgsFeatureIds ids;
666  ids.insert( mFilterModel->rowToId( currentIndex ) );
667  QgsMapCanvas *canvas = mFilterModel->mapCanvas();
668  if ( canvas )
669  {
670  canvas->zoomToFeatureIds( mLayer, ids );
671  }
672 }
673 
674 void QgsDualView::panToCurrentFeature()
675 {
676  QModelIndex currentIndex = mTableView->currentIndex();
677  if ( !currentIndex.isValid() )
678  {
679  return;
680  }
681 
682  QgsFeatureIds ids;
683  ids.insert( mFilterModel->rowToId( currentIndex ) );
684  QgsMapCanvas *canvas = mFilterModel->mapCanvas();
685  if ( canvas )
686  {
687  canvas->panToFeatureIds( mLayer, ids );
688  }
689 }
690 
691 void QgsDualView::rebuildFullLayerCache()
692 {
693  connect( mLayerCache, &QgsVectorLayerCache::progress, this, &QgsDualView::progress, Qt::UniqueConnection );
694  connect( mLayerCache, &QgsVectorLayerCache::finished, this, &QgsDualView::finished, Qt::UniqueConnection );
695 
696  mLayerCache->setFullCache( true );
697 }
698 
699 void QgsDualView::previewExpressionChanged( const QString &expression )
700 {
701  mLayer->setDisplayExpression( expression );
702 }
703 
704 void QgsDualView::onSortColumnChanged()
705 {
707  cfg.setSortExpression( mFilterModel->sortExpression() );
708  cfg.setSortOrder( mFilterModel->sortOrder() );
710 }
711 
712 void QgsDualView::sortByPreviewExpression()
713 {
714  Qt::SortOrder sortOrder = Qt::AscendingOrder;
715  if ( mFeatureList->displayExpression() == sortExpression() )
716  {
717  sortOrder = mConfig.sortOrder() == Qt::AscendingOrder ? Qt::DescendingOrder : Qt::AscendingOrder;
718  }
719  setSortExpression( mFeatureList->displayExpression(), sortOrder );
720 }
721 
722 void QgsDualView::updateSelectedFeatures()
723 {
724  QgsFeatureRequest r = mMasterModel->request();
726  return; // already requested all features
727 
728  r.setFilterFids( masterModel()->layer()->selectedFeatureIds() );
729  mMasterModel->setRequest( r );
730  mMasterModel->loadLayer();
731  emit filterChanged();
732 }
733 
734 void QgsDualView::extentChanged()
735 {
736  QgsFeatureRequest r = mMasterModel->request();
737  if ( mMapCanvas && ( r.filterType() != QgsFeatureRequest::FilterNone || !r.filterRect().isNull() ) )
738  {
739  QgsRectangle rect = mMapCanvas->mapSettings().mapToLayerCoordinates( mLayer, mMapCanvas->extent() );
740  r.setFilterRect( rect );
741  mMasterModel->setRequest( r );
742  mMasterModel->loadLayer();
743  }
744  emit filterChanged();
745 }
746 
747 void QgsDualView::featureFormAttributeChanged()
748 {
749  mFeatureList->setCurrentFeatureEdited( true );
750 }
751 
753 {
754  mFilterModel->setFilteredFeatures( filteredFeatures );
755 }
756 
758 {
759  mMasterModel->setRequest( request );
760 }
761 
763 {
764  mTableView->setFeatureSelectionManager( featureSelectionManager );
765  mFeatureList->setFeatureSelectionManager( featureSelectionManager );
766 
767  if ( mFeatureSelectionManager && mFeatureSelectionManager->parent() == this )
768  delete mFeatureSelectionManager;
769 
770  mFeatureSelectionManager = featureSelectionManager;
771 }
772 
774 {
775  mLayer->setAttributeTableConfig( config );
776  mFilterModel->setAttributeTableConfig( config );
777  mTableView->setAttributeTableConfig( config );
778  mConfig = config;
779 }
780 
781 void QgsDualView::setSortExpression( const QString &sortExpression, Qt::SortOrder sortOrder )
782 {
783  if ( sortExpression.isNull() )
784  mFilterModel->sort( -1 );
785  else
786  mFilterModel->sort( sortExpression, sortOrder );
787 
788  mConfig.setSortExpression( sortExpression );
789  mConfig.setSortOrder( sortOrder );
790  setAttributeTableConfig( mConfig );
791 }
792 
794 {
795  return mFilterModel->sortExpression();
796 }
797 
798 void QgsDualView::progress( int i, bool &cancel )
799 {
800  if ( !mProgressDlg )
801  {
802  mProgressDlg = new QProgressDialog( tr( "Loading features..." ), tr( "Abort" ), 0, 0, this );
803  mProgressDlg->setWindowTitle( tr( "Attribute table" ) );
804  mProgressDlg->setWindowModality( Qt::WindowModal );
805  mProgressDlg->show();
806  }
807 
808  mProgressDlg->setLabelText( tr( "%1 features loaded." ).arg( i ) );
809  QCoreApplication::processEvents();
810 
811  cancel = mProgressDlg && mProgressDlg->wasCanceled();
812 }
813 
814 void QgsDualView::finished()
815 {
816  delete mProgressDlg;
817  mProgressDlg = nullptr;
818 }
819 
820 /*
821  * QgsAttributeTableAction
822  */
823 
825 {
826  mDualView->masterModel()->executeAction( mAction, mFieldIdx );
827 }
828 
830 {
831  QgsFeatureIds editedIds;
832  editedIds << mDualView->masterModel()->rowToId( mFieldIdx.row() );
833  mDualView->setCurrentEditSelection( editedIds );
834  mDualView->setView( QgsDualView::AttributeEditor );
835 }
836 
837 /*
838  * QgsAttributeTableMapLayerAction
839  */
840 
842 {
843  mDualView->masterModel()->executeMapLayerAction( mAction, mFieldIdx );
844 }
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:70
QgsFeatureId rowToId(const QModelIndex &row)
Returns the feature id for a given model index.
QgsVectorLayer * layer() const
Returns the layer this filter acts on.
void setFilterMode(QgsAttributeTableFilterModel::FilterMode filterMode)
Set the filter mode.
A rectangle specified with double values.
Definition: qgsrectangle.h:38
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:73
void setExpressionText(const QString &text)
virtual QgsVectorDataProvider::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:54
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:160
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:54
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:519
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:54
void loadRecent(const QString &collection="generic")
Loads the recent expressions from the given collection.
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.
QgsDualView(QWidget *parent=0)
Constructor.
Definition: qgsdualview.cpp:42
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:61
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.
const QgsRectangle & filterRect() const
Returns 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:73
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.
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...
Show a list of the features, where one can be chosen and the according attribute dialog will be prese...
Definition: qgsdualview.h:67
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.
QgsFields fields() const override
Returns the list of fields of this layer.
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.
static QgsEditorWidgetRegistry * editorWidgetRegistry()
Returns the global editor widget registry, used for managing all known edit widget factories...
Definition: qgsgui.cpp:40
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
QgsFeatureRequest & setFilterRect(const QgsRectangle &rectangle)
Sets the rectangle from which features will be taken.
QgsFeatureIds filteredFeatures()
Get a list of currently visible feature ids.
Definition: qgsdualview.h:153
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:46
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:117
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:180
void zoomToFeatureIds(QgsVectorLayer *layer, const QgsFeatureIds &ids)
Set canvas extent to the bounding box of a set of features.
No filter is applied.
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.
QVariant value(const QString &key, const QVariant &defaultValue=QVariant(), const Section section=NoSection) const
Returns the value for setting key.
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.
QgsVectorDataProvider * dataProvider() override
Returns the layer&#39;s data provider.
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
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:41
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.
QgsPointXY mapToLayerCoordinates(const QgsMapLayer *layer, QgsPointXY point) const
transform point coordinates from output CRS to layer&#39;s CRS
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.
static QgsMapLayerActionRegistry * mapLayerActionRegistry()
Returns the global map layer action registry, used for registering map layer actions.
Definition: qgsgui.cpp:55
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.
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.