QGIS API Documentation  2.99.0-Master (7d4f81d)
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 #if QT_VERSION < QT_VERSION_CHECK(5, 6, 0)
478  menu->addAction( action.name(), a, SLOT( execute() ) );
479 #else
480  menu->addAction( action.name(), a, &QgsAttributeTableAction::execute );
481 #endif
482  }
483  }
484 
485  //add actions from QgsMapLayerActionRegistry to context menu
486  QList<QgsMapLayerAction *> registeredActions = QgsGui::mapLayerActionRegistry()->mapLayerActions( mLayer );
487  if ( !registeredActions.isEmpty() )
488  {
489  //add a separator between user defined and standard actions
490  menu->addSeparator();
491 
492  Q_FOREACH ( QgsMapLayerAction *action, registeredActions )
493  {
494  QgsAttributeTableMapLayerAction *a = new QgsAttributeTableMapLayerAction( action->text(), this, action, sourceIndex );
495 #if QT_VERSION < QT_VERSION_CHECK(5, 6, 0)
496  menu->addAction( action->text(), a, SLOT( execut() ) );
497 #else
498  menu->addAction( action->text(), a, &QgsAttributeTableMapLayerAction::execute );
499 #endif
500  }
501  }
502 
503  menu->addSeparator();
504  QgsAttributeTableAction *a = new QgsAttributeTableAction( tr( "Open form" ), this, QString(), sourceIndex );
505 #if QT_VERSION < QT_VERSION_CHECK(5, 6, 0)
506  menu->addAction( tr( "Open form" ), a, SLOT( featureForm() ) );
507 #else
508  menu->addAction( tr( "Open form" ), a, &QgsAttributeTableAction::featureForm );
509 #endif
510 }
511 
512 void QgsDualView::showViewHeaderMenu( QPoint point )
513 {
514  int col = mTableView->columnAt( point.x() );
515 
516  delete mHorizontalHeaderMenu;
517  mHorizontalHeaderMenu = new QMenu( this );
518 
519  QAction *hide = new QAction( tr( "&Hide column" ), mHorizontalHeaderMenu );
520  connect( hide, &QAction::triggered, this, &QgsDualView::hideColumn );
521  hide->setData( col );
522  mHorizontalHeaderMenu->addAction( hide );
523  QAction *setWidth = new QAction( tr( "&Set width..." ), mHorizontalHeaderMenu );
524  connect( setWidth, &QAction::triggered, this, &QgsDualView::resizeColumn );
525  setWidth->setData( col );
526  mHorizontalHeaderMenu->addAction( setWidth );
527  QAction *optimizeWidth = new QAction( tr( "&Autosize" ), mHorizontalHeaderMenu );
528  connect( optimizeWidth, &QAction::triggered, this, &QgsDualView::autosizeColumn );
529  optimizeWidth->setData( col );
530  mHorizontalHeaderMenu->addAction( optimizeWidth );
531 
532  mHorizontalHeaderMenu->addSeparator();
533  QAction *organize = new QAction( tr( "&Organize columns..." ), mHorizontalHeaderMenu );
534  connect( organize, &QAction::triggered, this, &QgsDualView::organizeColumns );
535  mHorizontalHeaderMenu->addAction( organize );
536  QAction *sort = new QAction( tr( "&Sort..." ), mHorizontalHeaderMenu );
537  connect( sort, &QAction::triggered, this, &QgsDualView::modifySort );
538  mHorizontalHeaderMenu->addAction( sort );
539 
540  mHorizontalHeaderMenu->popup( mTableView->horizontalHeader()->mapToGlobal( point ) );
541 }
542 
543 void QgsDualView::organizeColumns()
544 {
545  if ( !mLayer )
546  {
547  return;
548  }
549 
550  QgsOrganizeTableColumnsDialog dialog( mLayer, this );
551  if ( dialog.exec() == QDialog::Accepted )
552  {
553  QgsAttributeTableConfig config = dialog.config();
554  setAttributeTableConfig( config );
555  }
556 }
557 
558 void QgsDualView::tableColumnResized( int column, int width )
559 {
560  QgsAttributeTableConfig config = mConfig;
561  int sourceCol = config.mapVisibleColumnToIndex( column );
562  if ( sourceCol >= 0 )
563  {
564  config.setColumnWidth( sourceCol, width );
565  setAttributeTableConfig( config );
566  }
567 }
568 
569 void QgsDualView::hideColumn()
570 {
571  QAction *action = qobject_cast<QAction *>( sender() );
572  int col = action->data().toInt();
573  QgsAttributeTableConfig config = mConfig;
574  int sourceCol = mConfig.mapVisibleColumnToIndex( col );
575  if ( sourceCol >= 0 )
576  {
577  config.setColumnHidden( sourceCol, true );
578  setAttributeTableConfig( config );
579  }
580 }
581 
582 void QgsDualView::resizeColumn()
583 {
584  QAction *action = qobject_cast<QAction *>( sender() );
585  int col = action->data().toInt();
586  if ( col < 0 )
587  return;
588 
589  QgsAttributeTableConfig config = mConfig;
590  int sourceCol = config.mapVisibleColumnToIndex( col );
591  if ( sourceCol >= 0 )
592  {
593  bool ok = false;
594  int width = QInputDialog::getInt( this, tr( "Set column width" ), tr( "Enter column width" ),
595  mTableView->columnWidth( col ),
596  0, 1000, 10, &ok );
597  if ( ok )
598  {
599  config.setColumnWidth( sourceCol, width );
600  setAttributeTableConfig( config );
601  }
602  }
603 }
604 
605 void QgsDualView::autosizeColumn()
606 {
607  QAction *action = qobject_cast<QAction *>( sender() );
608  int col = action->data().toInt();
609  mTableView->resizeColumnToContents( col );
610 }
611 
612 void QgsDualView::modifySort()
613 {
614  if ( !mLayer )
615  return;
616 
617  QgsAttributeTableConfig config = mConfig;
618 
619  QDialog orderByDlg;
620  orderByDlg.setWindowTitle( tr( "Configure Attribute Table Sort Order" ) );
621  QDialogButtonBox *dialogButtonBox = new QDialogButtonBox( QDialogButtonBox::Ok | QDialogButtonBox::Cancel );
622  QGridLayout *layout = new QGridLayout();
623  connect( dialogButtonBox, &QDialogButtonBox::accepted, &orderByDlg, &QDialog::accept );
624  connect( dialogButtonBox, &QDialogButtonBox::rejected, &orderByDlg, &QDialog::reject );
625  orderByDlg.setLayout( layout );
626 
627  QGroupBox *sortingGroupBox = new QGroupBox();
628  sortingGroupBox->setTitle( tr( "Defined sort order in attribute table" ) );
629  sortingGroupBox->setCheckable( true );
630  sortingGroupBox->setChecked( !sortExpression().isEmpty() );
631  layout->addWidget( sortingGroupBox );
632  sortingGroupBox->setLayout( new QGridLayout() );
633 
634  QgsExpressionBuilderWidget *expressionBuilder = new QgsExpressionBuilderWidget();
636  expressionBuilder->setExpressionContext( context );
637  expressionBuilder->setLayer( mLayer );
638  expressionBuilder->loadFieldNames();
639  expressionBuilder->loadRecent( QStringLiteral( "generic" ) );
640  expressionBuilder->setExpressionText( sortExpression().isEmpty() ? mLayer->displayExpression() : sortExpression() );
641 
642  sortingGroupBox->layout()->addWidget( expressionBuilder );
643 
644  QCheckBox *cbxSortAscending = new QCheckBox( tr( "Sort ascending" ) );
645  cbxSortAscending->setChecked( config.sortOrder() == Qt::AscendingOrder );
646  sortingGroupBox->layout()->addWidget( cbxSortAscending );
647 
648  layout->addWidget( dialogButtonBox );
649  if ( orderByDlg.exec() )
650  {
651  Qt::SortOrder sortOrder = cbxSortAscending->isChecked() ? Qt::AscendingOrder : Qt::DescendingOrder;
652  if ( sortingGroupBox->isChecked() )
653  {
654  setSortExpression( expressionBuilder->expressionText(), sortOrder );
655  config.setSortExpression( expressionBuilder->expressionText() );
656  config.setSortOrder( sortOrder );
657  }
658  else
659  {
660  setSortExpression( QString(), sortOrder );
661  config.setSortExpression( QString() );
662  }
663 
664  setAttributeTableConfig( config );
665  }
666 }
667 
668 void QgsDualView::zoomToCurrentFeature()
669 {
670  QModelIndex currentIndex = mTableView->currentIndex();
671  if ( !currentIndex.isValid() )
672  {
673  return;
674  }
675 
676  QgsFeatureIds ids;
677  ids.insert( mFilterModel->rowToId( currentIndex ) );
678  QgsMapCanvas *canvas = mFilterModel->mapCanvas();
679  if ( canvas )
680  {
681  canvas->zoomToFeatureIds( mLayer, ids );
682  }
683 }
684 
685 void QgsDualView::panToCurrentFeature()
686 {
687  QModelIndex currentIndex = mTableView->currentIndex();
688  if ( !currentIndex.isValid() )
689  {
690  return;
691  }
692 
693  QgsFeatureIds ids;
694  ids.insert( mFilterModel->rowToId( currentIndex ) );
695  QgsMapCanvas *canvas = mFilterModel->mapCanvas();
696  if ( canvas )
697  {
698  canvas->panToFeatureIds( mLayer, ids );
699  }
700 }
701 
702 void QgsDualView::rebuildFullLayerCache()
703 {
704  connect( mLayerCache, &QgsVectorLayerCache::progress, this, &QgsDualView::progress, Qt::UniqueConnection );
705  connect( mLayerCache, &QgsVectorLayerCache::finished, this, &QgsDualView::finished, Qt::UniqueConnection );
706 
707  mLayerCache->setFullCache( true );
708 }
709 
710 void QgsDualView::previewExpressionChanged( const QString &expression )
711 {
712  mLayer->setDisplayExpression( expression );
713 }
714 
715 void QgsDualView::onSortColumnChanged()
716 {
718  cfg.setSortExpression( mFilterModel->sortExpression() );
719  cfg.setSortOrder( mFilterModel->sortOrder() );
721 }
722 
723 void QgsDualView::sortByPreviewExpression()
724 {
725  Qt::SortOrder sortOrder = Qt::AscendingOrder;
726  if ( mFeatureList->displayExpression() == sortExpression() )
727  {
728  sortOrder = mConfig.sortOrder() == Qt::AscendingOrder ? Qt::DescendingOrder : Qt::AscendingOrder;
729  }
730  setSortExpression( mFeatureList->displayExpression(), sortOrder );
731 }
732 
733 void QgsDualView::updateSelectedFeatures()
734 {
735  QgsFeatureRequest r = mMasterModel->request();
737  return; // already requested all features
738 
739  r.setFilterFids( masterModel()->layer()->selectedFeatureIds() );
740  mMasterModel->setRequest( r );
741  mMasterModel->loadLayer();
742  emit filterChanged();
743 }
744 
745 void QgsDualView::extentChanged()
746 {
747  QgsFeatureRequest r = mMasterModel->request();
748  if ( mMapCanvas && ( r.filterType() != QgsFeatureRequest::FilterNone || !r.filterRect().isNull() ) )
749  {
750  QgsRectangle rect = mMapCanvas->mapSettings().mapToLayerCoordinates( mLayer, mMapCanvas->extent() );
751  r.setFilterRect( rect );
752  mMasterModel->setRequest( r );
753  mMasterModel->loadLayer();
754  }
755  emit filterChanged();
756 }
757 
758 void QgsDualView::featureFormAttributeChanged()
759 {
760  mFeatureList->setCurrentFeatureEdited( true );
761 }
762 
764 {
765  mFilterModel->setFilteredFeatures( filteredFeatures );
766 }
767 
769 {
770  mMasterModel->setRequest( request );
771 }
772 
774 {
775  mTableView->setFeatureSelectionManager( featureSelectionManager );
776  mFeatureList->setFeatureSelectionManager( featureSelectionManager );
777 
778  if ( mFeatureSelectionManager && mFeatureSelectionManager->parent() == this )
779  delete mFeatureSelectionManager;
780 
781  mFeatureSelectionManager = featureSelectionManager;
782 }
783 
785 {
786  mConfig = config;
787  mLayer->setAttributeTableConfig( config );
788  mFilterModel->setAttributeTableConfig( config );
789  mTableView->setAttributeTableConfig( config );
790 }
791 
792 void QgsDualView::setSortExpression( const QString &sortExpression, Qt::SortOrder sortOrder )
793 {
794  if ( sortExpression.isNull() )
795  mFilterModel->sort( -1 );
796  else
797  mFilterModel->sort( sortExpression, sortOrder );
798 
799  mConfig.setSortExpression( sortExpression );
800  mConfig.setSortOrder( sortOrder );
801  setAttributeTableConfig( mConfig );
802 }
803 
805 {
806  return mFilterModel->sortExpression();
807 }
808 
809 void QgsDualView::progress( int i, bool &cancel )
810 {
811  if ( !mProgressDlg )
812  {
813  mProgressDlg = new QProgressDialog( tr( "Loading features..." ), tr( "Abort" ), 0, 0, this );
814  mProgressDlg->setWindowTitle( tr( "Attribute Table" ) );
815  mProgressDlg->setWindowModality( Qt::WindowModal );
816  mProgressDlg->show();
817  }
818 
819  mProgressDlg->setLabelText( tr( "%1 features loaded." ).arg( i ) );
820  QCoreApplication::processEvents();
821 
822  cancel = mProgressDlg && mProgressDlg->wasCanceled();
823 }
824 
825 void QgsDualView::finished()
826 {
827  delete mProgressDlg;
828  mProgressDlg = nullptr;
829 }
830 
831 /*
832  * QgsAttributeTableAction
833  */
834 
836 {
837  mDualView->masterModel()->executeAction( mAction, mFieldIdx );
838 }
839 
841 {
842  QgsFeatureIds editedIds;
843  editedIds << mDualView->masterModel()->rowToId( mFieldIdx.row() );
844  mDualView->setCurrentEditSelection( editedIds );
845  mDualView->setView( QgsDualView::AttributeEditor );
846 }
847 
848 /*
849  * QgsAttributeTableMapLayerAction
850  */
851 
853 {
854  mDualView->masterModel()->executeMapLayerAction( mAction, mFieldIdx );
855 }
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:43
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:187
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:58
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.
An action which can run on map layers.
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.