QGIS API Documentation  2.18.21-Las Palmas (9fba24a)
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"
29 
30 #include <QClipboard>
31 #include <QDialog>
32 #include <QMenu>
33 #include <QMessageBox>
34 #include <QProgressDialog>
35 #include <QSettings>
36 #include <QGroupBox>
37 #include <QInputDialog>
38 
40  : QStackedWidget( parent )
41  , mEditorContext()
42  , mMasterModel( nullptr )
43  , mFilterModel( nullptr )
44  , mFeatureListModel( nullptr )
45  , mAttributeForm( nullptr )
46  , mHorizontalHeaderMenu( nullptr )
47  , mLayerCache( nullptr )
48  , mProgressDlg( nullptr )
49  , mFeatureSelectionManager( nullptr )
50  , mAttributeEditorScrollArea( nullptr )
51  , mMapCanvas( nullptr )
52 {
53  setupUi( this );
54 
55  mConditionalFormatWidget->hide();
56 
57  mPreviewActionMapper = new QSignalMapper( this );
58 
59  mPreviewColumnsMenu = new QMenu( this );
60  mActionPreviewColumnsMenu->setMenu( mPreviewColumnsMenu );
61 
62  // Set preview icon
63  mActionExpressionPreview->setIcon( QgsApplication::getThemeIcon( "/mIconExpressionPreview.svg" ) );
64 
65  // Connect layer list preview signals
66  connect( mActionExpressionPreview, SIGNAL( triggered() ), SLOT( previewExpressionBuilder() ) );
67  connect( mPreviewActionMapper, SIGNAL( mapped( QObject* ) ), SLOT( previewColumnChanged( QObject* ) ) );
68  connect( mFeatureList, SIGNAL( displayExpressionChanged( QString ) ), this, SLOT( previewExpressionChanged( QString ) ) );
69 }
70 
71 void QgsDualView::init( QgsVectorLayer *layer, QgsMapCanvas *mapCanvas, const QgsFeatureRequest &request, const QgsAttributeEditorContext &context, bool loadFeatures )
72 {
73  mMapCanvas = mapCanvas;
74 
75  if ( !layer )
76  return;
77 
78  mLayer = layer;
79 
80  mEditorContext = context;
81 
82  connect( mTableView, SIGNAL( willShowContextMenu( QMenu*, QModelIndex ) ), this, SLOT( viewWillShowContextMenu( QMenu*, QModelIndex ) ) );
83  mTableView->horizontalHeader()->setContextMenuPolicy( Qt::CustomContextMenu );
84  connect( mTableView->horizontalHeader(), SIGNAL( customContextMenuRequested( QPoint ) ), this, SLOT( showViewHeaderMenu( QPoint ) ) );
85  connect( mTableView, SIGNAL( columnResized( int, int ) ), this, SLOT( tableColumnResized( int, int ) ) );
86 
87  initLayerCache( !( request.flags() & QgsFeatureRequest::NoGeometry ) || !request.filterRect().isNull() );
88  initModels( mapCanvas, request, loadFeatures );
89 
90  mConditionalFormatWidget->setLayer( mLayer );
91 
92  mTableView->setModel( mFilterModel );
93  mFeatureList->setModel( mFeatureListModel );
94  delete mAttributeForm;
95  mAttributeForm = new QgsAttributeForm( mLayer, QgsFeature(), mEditorContext );
96  if ( !context.parentContext() )
97  {
98  mAttributeEditorScrollArea = new QScrollArea();
99  mAttributeEditorScrollArea->setWidgetResizable( true );
100  mAttributeEditor->layout()->addWidget( mAttributeEditorScrollArea );
101  mAttributeEditorScrollArea->setWidget( mAttributeForm );
102  }
103  else
104  {
105  mAttributeEditor->layout()->addWidget( mAttributeForm );
106  }
107 
108  connect( mAttributeForm, SIGNAL( attributeChanged( QString, QVariant ) ), this, SLOT( featureFormAttributeChanged() ) );
109  connect( mAttributeForm, SIGNAL( modeChanged( QgsAttributeForm::Mode ) ), this, SIGNAL( formModeChanged( QgsAttributeForm::Mode ) ) );
110  connect( mMasterModel, SIGNAL( modelChanged() ), mAttributeForm, SLOT( refreshFeature() ) );
112  connect( mFilterModel, SIGNAL( sortColumnChanged( int, Qt::SortOrder ) ), this, SLOT( onSortColumnChanged() ) );
113  if ( mFeatureListPreviewButton->defaultAction() )
114  mFeatureList->setDisplayExpression( mDisplayExpression );
115  else
116  columnBoxInit();
117 
118  // This slows down load of the attribute table heaps and uses loads of memory.
119  //mTableView->resizeColumnsToContents();
120 
121  mFeatureList->setEditSelection( QgsFeatureIds() << mFeatureListModel->idxToFid( mFeatureListModel->index( 0, 0 ) ) );
122 }
123 
125 {
126  // load fields
127  QList<QgsField> fields = mLayer->fields().toList();
128 
129  QString defaultField;
130 
131  // default expression: saved value
132  QString displayExpression = mLayer->displayExpression();
133 
134  // if no display expression is saved: use display field instead
135  if ( displayExpression.isEmpty() )
136  {
137  if ( !mLayer->displayField().isEmpty() )
138  {
139  defaultField = mLayer->displayField();
140  displayExpression = QString( "COALESCE(\"%1\", '<NULL>')" ).arg( defaultField );
141  }
142  }
143 
144  // if neither display expression nor display field is saved...
145  if ( displayExpression.isEmpty() )
146  {
147  QgsAttributeList pkAttrs = mLayer->pkAttributeList();
148 
149  if ( !pkAttrs.isEmpty() )
150  {
151  if ( pkAttrs.size() == 1 )
152  defaultField = pkAttrs.at( 0 );
153 
154  // ... If there are primary key(s) defined
155  QStringList pkFields;
156 
157  Q_FOREACH ( int attr, pkAttrs )
158  {
159  pkFields.append( "COALESCE(\"" + fields[attr].name() + "\", '<NULL>')" );
160  }
161 
162  displayExpression = pkFields.join( "||', '||" );
163  }
164  else if ( !fields.isEmpty() )
165  {
166  if ( fields.size() == 1 )
167  defaultField = fields.at( 0 ).name();
168 
169  // ... concat all fields
170  QStringList fieldNames;
171  Q_FOREACH ( const QgsField& field, fields )
172  {
173  fieldNames.append( "COALESCE(\"" + field.name() + "\", '<NULL>')" );
174  }
175 
176  displayExpression = fieldNames.join( "||', '||" );
177  }
178  else
179  {
180  // ... there isn't really much to display
181  displayExpression = "'[Please define preview text]'";
182  }
183  }
184 
185  mFeatureListPreviewButton->addAction( mActionExpressionPreview );
186  mFeatureListPreviewButton->addAction( mActionPreviewColumnsMenu );
187 
188  Q_FOREACH ( const QgsField& field, fields )
189  {
190  int fieldIndex = mLayer->fieldNameIndex( field.name() );
191  if ( fieldIndex == -1 )
192  continue;
193 
194  if ( mLayer->editFormConfig()->widgetType( fieldIndex ) != "Hidden" )
195  {
196  QIcon icon = mLayer->fields().iconForField( fieldIndex );
197  QString text = field.name();
198 
199  // Generate action for the preview popup button of the feature list
200  QAction* previewAction = new QAction( icon, text, mFeatureListPreviewButton );
201  mPreviewActionMapper->setMapping( previewAction, previewAction );
202  connect( previewAction, SIGNAL( triggered() ), mPreviewActionMapper, SLOT( map() ) );
203  mPreviewColumnsMenu->addAction( previewAction );
204 
205  if ( text == defaultField )
206  {
207  mFeatureListPreviewButton->setDefaultAction( previewAction );
208  }
209  }
210  }
211 
212  // If there is no single field found as preview
213  if ( !mFeatureListPreviewButton->defaultAction() )
214  {
215  mFeatureList->setDisplayExpression( displayExpression );
216  mFeatureListPreviewButton->setDefaultAction( mActionExpressionPreview );
217  mDisplayExpression = mFeatureList->displayExpression();
218  }
219  else
220  {
221  mFeatureListPreviewButton->defaultAction()->trigger();
222  }
223 
224  QAction* sortByPreviewExpression = new QAction( QgsApplication::getThemeIcon( "sort.svg" ), tr( "Sort by preview expression" ), this );
225  connect( sortByPreviewExpression, SIGNAL( triggered( bool ) ), this, SLOT( sortByPreviewExpression() ) );
226  mFeatureListPreviewButton->addAction( sortByPreviewExpression );
227 }
228 
230 {
231  setCurrentIndex( view );
232 }
233 
235 {
236  return static_cast< QgsDualView::ViewMode >( currentIndex() );
237 }
238 
240 {
241  // cleanup any existing connections
242  switch ( mFilterModel->filterMode() )
243  {
245  disconnect( mMapCanvas, SIGNAL( extentsChanged() ), this, SLOT( extentChanged() ) );
246  break;
247 
251  break;
252 
254  disconnect( masterModel()->layer(), SIGNAL( selectionChanged() ), this,
255  SLOT( updateSelectedFeatures() ) );
256  break;
257  }
258 
259  QgsFeatureRequest r = mMasterModel->request();
260  bool needsGeometry = filterMode == QgsAttributeTableFilterModel::ShowVisible;
261 
262  bool requiresTableReload = ( r.filterType() != QgsFeatureRequest::FilterNone || !r.filterRect().isNull() ) // previous request was subset
263  || ( needsGeometry && r.flags() & QgsFeatureRequest::NoGeometry ) // no geometry for last request
264  || ( mMasterModel->rowCount() == 0 ); // no features
265 
266  if ( !needsGeometry )
268  else
272  r.disableFilter();
273 
274  // setup new connections and filter request parameters
275  switch ( filterMode )
276  {
278  connect( mMapCanvas, SIGNAL( extentsChanged() ), this, SLOT( extentChanged() ) );
279  if ( mMapCanvas )
280  {
281  QgsRectangle rect = mMapCanvas->mapSettings().mapToLayerCoordinates( mLayer, mMapCanvas->extent() );
282  r.setFilterRect( rect );
283  }
284  break;
285 
289  break;
290 
292  connect( masterModel()->layer(), SIGNAL( selectionChanged() ), this, SLOT( updateSelectedFeatures() ) );
293  if ( masterModel()->layer()->selectedFeatureCount() > 0 )
294  r.setFilterFids( masterModel()->layer()->selectedFeaturesIds() );
295  break;
296  }
297 
298  if ( requiresTableReload )
299  {
300  mMasterModel->setRequest( r );
301  whileBlocking( mLayerCache )->setCacheGeometry( needsGeometry );
302  mMasterModel->loadLayer();
303  }
304 
305  //update filter model
306  mFilterModel->setFilterMode( filterMode );
307  emit filterChanged();
308 }
309 
310 void QgsDualView::setSelectedOnTop( bool selectedOnTop )
311 {
312  mFilterModel->setSelectedOnTop( selectedOnTop );
313 }
314 
315 void QgsDualView::initLayerCache( bool cacheGeometry )
316 {
317  // Initialize the cache
318  QSettings settings;
319  int cacheSize = settings.value( "/qgis/attributeTableRowCache", "10000" ).toInt();
320  mLayerCache = new QgsVectorLayerCache( mLayer, cacheSize, this );
321  mLayerCache->setCacheGeometry( cacheGeometry );
322  if ( 0 == cacheSize || 0 == ( QgsVectorDataProvider::SelectAtId & mLayer->dataProvider()->capabilities() ) )
323  {
324  connect( mLayerCache, SIGNAL( invalidated() ), this, SLOT( rebuildFullLayerCache() ) );
325  rebuildFullLayerCache();
326  }
327 }
328 
329 void QgsDualView::initModels( QgsMapCanvas *mapCanvas, const QgsFeatureRequest &request, bool loadFeatures )
330 {
331  delete mFeatureListModel;
332  delete mFilterModel;
333  delete mMasterModel;
334 
335  mMasterModel = new QgsAttributeTableModel( mLayerCache, this );
336  mMasterModel->setRequest( request );
337  mMasterModel->setEditorContext( mEditorContext );
338  mMasterModel->setExtraColumns( 1 ); // Add one extra column which we can "abuse" as an action column
339 
340  connect( mMasterModel, SIGNAL( progress( int, bool & ) ), this, SLOT( progress( int, bool & ) ) );
341  connect( mMasterModel, SIGNAL( finished() ), this, SLOT( finished() ) );
342 
343  connect( mConditionalFormatWidget, SIGNAL( rulesUpdated( QString ) ), mMasterModel, SLOT( fieldConditionalStyleChanged( QString ) ) );
344 
345  if ( loadFeatures )
346  mMasterModel->loadLayer();
347 
348  mFilterModel = new QgsAttributeTableFilterModel( mapCanvas, mMasterModel, mMasterModel );
349 
350  connect( mFeatureList, SIGNAL( displayExpressionChanged( QString ) ), this, SIGNAL( displayExpressionChanged( QString ) ) );
351 
352  mFeatureListModel = new QgsFeatureListModel( mFilterModel, mFilterModel );
353 }
354 
355 void QgsDualView::on_mFeatureList_aboutToChangeEditSelection( bool& ok )
356 {
357  if ( mLayer->isEditable() && !mAttributeForm->save() )
358  ok = false;
359 }
360 
361 void QgsDualView::on_mFeatureList_currentEditSelectionChanged( const QgsFeature &feat )
362 {
363  if ( !mLayer->isEditable() || mAttributeForm->save() )
364  {
365  mAttributeForm->setFeature( feat );
367  }
368  else
369  {
370  // Couldn't save feature
371  }
372 }
373 
375 {
376  mFeatureList->setCurrentFeatureEdited( false );
377  mFeatureList->setEditSelection( fids );
378 }
379 
381 {
382  return mAttributeForm->save();
383 }
384 
386 {
387  mConditionalFormatWidget->setVisible( !mConditionalFormatWidget->isVisible() );
388  mConditionalFormatWidget->viewRules();
389 }
390 
392 {
393  if ( enabled )
395 
397 }
398 
400 {
401  if ( enabled )
402  {
404  mAttributeForm->setMode( QgsAttributeForm::SearchMode );
405  }
406  else
407  {
408  mAttributeForm->setMode( QgsAttributeForm::SingleEditMode );
409  }
410 }
411 
412 void QgsDualView::previewExpressionBuilder()
413 {
414  // Show expression builder
415  QgsExpressionContext context;
419 
420  QgsExpressionBuilderDialog dlg( mLayer, mFeatureList->displayExpression(), this, "generic", context );
421  dlg.setWindowTitle( tr( "Expression based preview" ) );
422  dlg.setExpressionText( mFeatureList->displayExpression() );
423 
424  if ( dlg.exec() == QDialog::Accepted )
425  {
426  mFeatureList->setDisplayExpression( dlg.expressionText() );
427  mFeatureListPreviewButton->setDefaultAction( mActionExpressionPreview );
428  mFeatureListPreviewButton->setPopupMode( QToolButton::MenuButtonPopup );
429  }
430 
431  mDisplayExpression = mFeatureList->displayExpression();
432 }
433 
434 void QgsDualView::previewColumnChanged( QObject* action )
435 {
436  QAction* previewAction = qobject_cast< QAction* >( action );
437 
438  if ( previewAction )
439  {
440  if ( !mFeatureList->setDisplayExpression( QString( "COALESCE( \"%1\", '<NULL>' )" ).arg( previewAction->text() ) ) )
441  {
442  QMessageBox::warning( this,
443  tr( "Could not set preview column" ),
444  tr( "Could not set column '%1' as preview column.\nParser error:\n%2" )
445  .arg( previewAction->text(), mFeatureList->parserErrorString() )
446  );
447  }
448  else
449  {
450  mFeatureListPreviewButton->setDefaultAction( previewAction );
451  mFeatureListPreviewButton->setPopupMode( QToolButton::InstantPopup );
452  }
453  }
454 
455  mDisplayExpression = mFeatureList->displayExpression();
456 
457  Q_ASSERT( previewAction );
458 }
459 
461 {
462  return mMasterModel->rowCount();
463 }
464 
466 {
467  return mFilterModel->rowCount();
468 }
469 
471 {
472  QAction* action = qobject_cast<QAction*>( sender() );
473 
474  if ( action && action->data().isValid() && action->data().canConvert<QModelIndex>() )
475  {
476  QModelIndex index = action->data().value<QModelIndex>();
477  QVariant var = masterModel()->data( index, Qt::DisplayRole );
478  QApplication::clipboard()->setText( var.toString() );
479  }
480 }
481 
482 void QgsDualView::viewWillShowContextMenu( QMenu* menu, const QModelIndex& atIndex )
483 {
484  if ( !menu )
485  {
486  return;
487  }
488 
489 
490  QModelIndex sourceIndex = mFilterModel->mapToSource( atIndex );
491 
492  QAction *copyContentAction = new QAction( tr( "Copy cell content" ), this );
493  copyContentAction->setData( QVariant::fromValue<QModelIndex>( sourceIndex ) );
494  menu->addAction( copyContentAction );
495  connect( copyContentAction, SIGNAL( triggered() ), this, SLOT( copyCellContent() ) );
496 
497  QgsVectorLayer* vl = mFilterModel->layer();
498  QgsMapCanvas* canvas = mFilterModel->mapCanvas();
499  if ( canvas && vl && vl->geometryType() != QGis::NoGeometry )
500  {
501  menu->addAction( tr( "Zoom to feature" ), this, SLOT( zoomToCurrentFeature() ) );
502  }
503 
504  //add user-defined actions to context menu
505  if ( mLayer->actions()->size() != 0 )
506  {
507 
508  QAction *a = menu->addAction( tr( "Run layer action" ) );
509  a->setEnabled( false );
510 
511  for ( int i = 0; i < mLayer->actions()->size(); i++ )
512  {
513  const QgsAction &action = mLayer->actions()->at( i );
514 
515  if ( !action.runable() )
516  continue;
517 
518  QgsAttributeTableAction *a = new QgsAttributeTableAction( action.name(), this, i, sourceIndex );
519  menu->addAction( action.name(), a, SLOT( execute() ) );
520  }
521  }
522 
523  //add actions from QgsMapLayerActionRegistry to context menu
525  if ( !registeredActions.isEmpty() )
526  {
527  //add a separator between user defined and standard actions
528  menu->addSeparator();
529 
531  for ( actionIt = registeredActions.begin(); actionIt != registeredActions.end(); ++actionIt )
532  {
533  QgsAttributeTableMapLayerAction *a = new QgsAttributeTableMapLayerAction(( *actionIt )->text(), this, ( *actionIt ), sourceIndex );
534  menu->addAction(( *actionIt )->text(), a, SLOT( execute() ) );
535  }
536  }
537 
538  menu->addSeparator();
539  QgsAttributeTableAction *a = new QgsAttributeTableAction( tr( "Open form" ), this, -1, sourceIndex );
540  menu->addAction( tr( "Open form" ), a, SLOT( featureForm() ) );
541 }
542 
543 void QgsDualView::showViewHeaderMenu( QPoint point )
544 {
545  int col = mTableView->columnAt( point.x() );
546 
547  delete mHorizontalHeaderMenu;
548  mHorizontalHeaderMenu = new QMenu( this );
549 
550  QAction* hide = new QAction( tr( "&Hide column" ), mHorizontalHeaderMenu );
551  connect( hide, SIGNAL( triggered( bool ) ), this, SLOT( hideColumn() ) );
552  hide->setData( col );
553  mHorizontalHeaderMenu->addAction( hide );
554  QAction* setWidth = new QAction( tr( "&Set width..." ), mHorizontalHeaderMenu );
555  connect( setWidth, SIGNAL( triggered( bool ) ), this, SLOT( resizeColumn() ) );
556  setWidth->setData( col );
557  mHorizontalHeaderMenu->addAction( setWidth );
558  QAction* optimizeWidth = new QAction( tr( "&Autosize" ), mHorizontalHeaderMenu );
559  connect( optimizeWidth, SIGNAL( triggered( bool ) ), this, SLOT( autosizeColumn() ) );
560  optimizeWidth->setData( col );
561  mHorizontalHeaderMenu->addAction( optimizeWidth );
562 
563  mHorizontalHeaderMenu->addSeparator();
564  QAction* organize = new QAction( tr( "&Organize columns..." ), mHorizontalHeaderMenu );
565  connect( organize, SIGNAL( triggered( bool ) ), this, SLOT( organizeColumns() ) );
566  mHorizontalHeaderMenu->addAction( organize );
567  QAction* sort = new QAction( tr( "&Sort..." ), mHorizontalHeaderMenu );
568  connect( sort, SIGNAL( triggered( bool ) ), this, SLOT( modifySort() ) );
569  mHorizontalHeaderMenu->addAction( sort );
570 
571  mHorizontalHeaderMenu->popup( mTableView->horizontalHeader()->mapToGlobal( point ) );
572 }
573 
574 void QgsDualView::organizeColumns()
575 {
576  if ( !mLayer )
577  {
578  return;
579  }
580 
581  QgsOrganizeTableColumnsDialog dialog( mLayer, this );
582  if ( dialog.exec() == QDialog::Accepted )
583  {
584  QgsAttributeTableConfig config = dialog.config();
585  setAttributeTableConfig( config );
586  }
587 }
588 
589 void QgsDualView::tableColumnResized( int column, int width )
590 {
591  QgsAttributeTableConfig config = mConfig;
592  int sourceCol = config.mapVisibleColumnToIndex( column );
593  if ( sourceCol >= 0 )
594  {
595  config.setColumnWidth( sourceCol, width );
596  setAttributeTableConfig( config );
597  }
598 }
599 
600 void QgsDualView::hideColumn()
601 {
602  QAction* action = qobject_cast<QAction*>( sender() );
603  int col = action->data().toInt();
604  QgsAttributeTableConfig config = mConfig;
605  int sourceCol = mConfig.mapVisibleColumnToIndex( col );
606  if ( sourceCol >= 0 )
607  {
608  config.setColumnHidden( sourceCol, true );
609  setAttributeTableConfig( config );
610  }
611 }
612 
613 void QgsDualView::resizeColumn()
614 {
615  QAction* action = qobject_cast<QAction*>( sender() );
616  int col = action->data().toInt();
617  if ( col < 0 )
618  return;
619 
620  QgsAttributeTableConfig config = mConfig;
621  int sourceCol = config.mapVisibleColumnToIndex( col );
622  if ( sourceCol >= 0 )
623  {
624  bool ok = false;
625  int width = QInputDialog::getInt( this, tr( "Set column width" ), tr( "Enter column width" ),
626  mTableView->columnWidth( col ),
627  0, 1000, 10, &ok );
628  if ( ok )
629  {
630  config.setColumnWidth( sourceCol, width );
631  setAttributeTableConfig( config );
632  }
633  }
634 }
635 
636 void QgsDualView::autosizeColumn()
637 {
638  QAction* action = qobject_cast<QAction*>( sender() );
639  int col = action->data().toInt();
640  mTableView->resizeColumnToContents( col );
641 }
642 
643 void QgsDualView::modifySort()
644 {
645  if ( !mLayer )
646  return;
647 
648  QgsAttributeTableConfig config = mConfig;
649 
650  QDialog orderByDlg;
651  orderByDlg.setWindowTitle( tr( "Configure attribute table sort order" ) );
652  QDialogButtonBox* dialogButtonBox = new QDialogButtonBox( QDialogButtonBox::Ok | QDialogButtonBox::Cancel );
653  QGridLayout* layout = new QGridLayout();
654  connect( dialogButtonBox, SIGNAL( accepted() ), &orderByDlg, SLOT( accept() ) );
655  connect( dialogButtonBox, SIGNAL( rejected() ), &orderByDlg, SLOT( reject() ) );
656  orderByDlg.setLayout( layout );
657 
658  QGroupBox* sortingGroupBox = new QGroupBox();
659  sortingGroupBox->setTitle( tr( "Defined sort order in attribute table" ) );
660  sortingGroupBox->setCheckable( true );
661  sortingGroupBox->setChecked( !sortExpression().isEmpty() );
662  layout->addWidget( sortingGroupBox );
663  sortingGroupBox->setLayout( new QGridLayout() );
664 
665  QgsExpressionBuilderWidget* expressionBuilder = new QgsExpressionBuilderWidget();
666  QgsExpressionContext context;
670  expressionBuilder->setExpressionContext( context );
671  expressionBuilder->setLayer( mLayer );
672  expressionBuilder->loadFieldNames();
673  expressionBuilder->loadRecent( "generic" );
674  expressionBuilder->setExpressionText( sortExpression().isEmpty() ? mLayer->displayExpression() : sortExpression() );
675 
676  sortingGroupBox->layout()->addWidget( expressionBuilder );
677 
678  QCheckBox* cbxSortAscending = new QCheckBox( tr( "Sort ascending" ) );
679  cbxSortAscending->setChecked( config.sortOrder() == Qt::AscendingOrder );
680  sortingGroupBox->layout()->addWidget( cbxSortAscending );
681 
682  layout->addWidget( dialogButtonBox );
683  if ( orderByDlg.exec() )
684  {
685  Qt::SortOrder sortOrder = cbxSortAscending->isChecked() ? Qt::AscendingOrder : Qt::DescendingOrder;
686  if ( sortingGroupBox->isChecked() )
687  {
688  setSortExpression( expressionBuilder->expressionText(), sortOrder );
689  config.setSortExpression( expressionBuilder->expressionText() );
690  config.setSortOrder( sortOrder );
691  }
692  else
693  {
694  setSortExpression( QString(), sortOrder );
695  config.setSortExpression( QString() );
696  }
697 
698  setAttributeTableConfig( config );
699  }
700 }
701 
702 void QgsDualView::zoomToCurrentFeature()
703 {
704  QModelIndex currentIndex = mTableView->currentIndex();
705  if ( !currentIndex.isValid() )
706  {
707  return;
708  }
709 
710  QgsFeatureIds ids;
711  ids.insert( mFilterModel->rowToId( currentIndex ) );
712  QgsMapCanvas* canvas = mFilterModel->mapCanvas();
713  if ( canvas )
714  {
715  canvas->zoomToFeatureIds( mLayer, ids );
716  }
717 }
718 
719 void QgsDualView::rebuildFullLayerCache()
720 {
721  connect( mLayerCache, SIGNAL( progress( int, bool& ) ), this, SLOT( progress( int, bool& ) ), Qt::UniqueConnection );
722  connect( mLayerCache, SIGNAL( finished() ), this, SLOT( finished() ), Qt::UniqueConnection );
723 
724  mLayerCache->setFullCache( true );
725 }
726 
727 void QgsDualView::previewExpressionChanged( const QString& expression )
728 {
729  mLayer->setDisplayExpression( expression );
730 }
731 
732 void QgsDualView::onSortColumnChanged()
733 {
735  cfg.setSortExpression( mFilterModel->sortExpression() );
736  cfg.setSortOrder( mFilterModel->sortOrder() );
738 }
739 
740 void QgsDualView::sortByPreviewExpression()
741 {
742  Qt::SortOrder sortOrder = Qt::AscendingOrder;
743  if ( mFeatureList->displayExpression() == sortExpression() )
744  {
745  sortOrder = mConfig.sortOrder() == Qt::AscendingOrder ? Qt::DescendingOrder : Qt::AscendingOrder;
746  }
747  setSortExpression( mFeatureList->displayExpression(), sortOrder );
748 }
749 
750 void QgsDualView::updateSelectedFeatures()
751 {
752  QgsFeatureRequest r = mMasterModel->request();
754  return; // already requested all features
755 
756  if ( masterModel()->layer()->selectedFeatureCount() > 0 )
757  r.setFilterFids( masterModel()->layer()->selectedFeaturesIds() );
758  else
759  r.disableFilter();
760  mMasterModel->setRequest( r );
761  mMasterModel->loadLayer();
762  emit filterChanged();
763 }
764 
765 void QgsDualView::extentChanged()
766 {
767  QgsFeatureRequest r = mMasterModel->request();
768  if ( mMapCanvas && ( r.filterType() != QgsFeatureRequest::FilterNone || !r.filterRect().isNull() ) )
769  {
770  QgsRectangle rect = mMapCanvas->mapSettings().mapToLayerCoordinates( mLayer, mMapCanvas->extent() );
771  r.setFilterRect( rect );
772  mMasterModel->setRequest( r );
773  mMasterModel->loadLayer();
774  }
775  emit filterChanged();
776 }
777 
778 void QgsDualView::featureFormAttributeChanged()
779 {
780  mFeatureList->setCurrentFeatureEdited( true );
781 }
782 
784 {
785  mFilterModel->setFilteredFeatures( filteredFeatures );
786 }
787 
789 {
790  mMasterModel->setRequest( request );
791 }
792 
794 {
795  mTableView->setFeatureSelectionManager( featureSelectionManager );
796  mFeatureList->setFeatureSelectionManager( featureSelectionManager );
797 
798  if ( mFeatureSelectionManager && mFeatureSelectionManager->parent() == this )
799  delete mFeatureSelectionManager;
800 
801  mFeatureSelectionManager = featureSelectionManager;
802 }
803 
805 {
806  mConfig = config;
807  mLayer->setAttributeTableConfig( config );
808  mFilterModel->setAttributeTableConfig( config );
809  mTableView->setAttributeTableConfig( config );
810 }
811 
812 void QgsDualView::setSortExpression( const QString& sortExpression, Qt::SortOrder sortOrder )
813 {
814  if ( sortExpression.isNull() )
815  mFilterModel->sort( -1 );
816  else
817  mFilterModel->sort( sortExpression, sortOrder );
818 
819  mConfig.setSortExpression( sortExpression );
820  mConfig.setSortOrder( sortOrder );
821  setAttributeTableConfig( mConfig );
822 }
823 
825 {
826  return mFilterModel->sortExpression();
827 }
828 
829 void QgsDualView::progress( int i, bool& cancel )
830 {
831  if ( !mProgressDlg )
832  {
833  mProgressDlg = new QProgressDialog( tr( "Loading features..." ), tr( "Abort" ), 0, 0, this );
834  mProgressDlg->setWindowTitle( tr( "Attribute table" ) );
835  mProgressDlg->setWindowModality( Qt::WindowModal );
836  mProgressDlg->show();
837  }
838 
839  mProgressDlg->setLabelText( tr( "%1 features loaded." ).arg( i ) );
841 
842  cancel = mProgressDlg && mProgressDlg->wasCanceled();
843 }
844 
845 void QgsDualView::finished()
846 {
847  delete mProgressDlg;
848  mProgressDlg = nullptr;
849 }
850 
851 /*
852  * QgsAttributeTableAction
853  */
854 
856 {
857  mDualView->masterModel()->executeAction( mAction, mFieldIdx );
858 }
859 
861 {
862  QgsFeatureIds editedIds;
863  editedIds << mDualView->masterModel()->rowToId( mFieldIdx.row() );
864  mDualView->setCurrentEditSelection( editedIds );
865  mDualView->setView( QgsDualView::AttributeEditor );
866 }
867 
868 /*
869  * QgsAttributeTableMapLayerAction
870  */
871 
873 {
874  mDualView->masterModel()->executeMapLayerAction( mAction, mFieldIdx );
875 }
QLayout * layout() const
void customContextMenuRequested(const QPoint &pos)
bool canConvert(Type t) const
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 rowToId(const QModelIndex &row)
Returns the feature id for a given model index.
QgsVectorLayer * layer() const
Returns the layer this filter acts on.
QgsVectorLayer * layer() const
Returns the layer this model uses as backend.
static unsigned index
void setFilterMode(QgsAttributeTableFilterModel::FilterMode filterMode)
Set the filter mode.
A rectangle specified with double values.
Definition: qgsrectangle.h:35
void setSortExpression(const QString &sortExpression)
Set the sort expression used for sorting.
void setWidget(QWidget *widget)
virtual void loadLayer()
Loads the layer into the model Preferably to be called, before using this model as source for any oth...
void setupUi(QWidget *widget)
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:71
QgsFeatureId idxToFid(const QModelIndex &index) const
QgsAttributeTableConfig config() const
Get the updated configuration.
const Flags & flags() const
QString name
Definition: qgsfield.h:52
void setSortOrder(const Qt::SortOrder &sortOrder)
Set the sort order.
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 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.
void addWidget(QWidget *widget, int row, int column, QFlags< Qt::AlignmentFlag > alignment)
void openConditionalStyles()
void setWindowModality(Qt::WindowModality windowModality)
QIcon iconForField(int fieldIdx) const
Returns an icon corresponding to a field index, based on the field&#39;s type and source.
Definition: qgsfield.cpp:529
QSet< QgsFeatureId > QgsFeatureIds
Definition: qgsfeature.h:439
QString sortExpression() const
Get the expression used for sorting the table and feature list.
This class contains context information for attribute editor widgets.
QObject * sender() const
QVariant data() const
static QIcon getThemeIcon(const QString &theName)
Helper to get a theme icon.
void setLayer(QgsVectorLayer *layer)
Sets layer in order to get the fields and values.
void setLabelText(const QString &text)
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:55
const T & at(int i) const
void loadRecent(const QString &collection="generic")
Loads the recent expressions from the given collection.
void addAction(QAction *action)
FilterType filterType() const
Return the filter type which is currently set on this request.
T value() const
int exec()
virtual QModelIndex mapToSource(const QModelIndex &proxyIndex) const override
int filteredFeatureCount()
Returns the number of features which are currently visible, according to the filter restrictions...
const QPixmap * icon() const
void setDisplayExpression(const QString &displayExpression)
Set the preview expression, used to create a human readable preview string.
QString join(const QString &separator) const
const_iterator insert(const T &value)
The feature class encapsulates a single feature including its id, geometry and a list of field/values...
Definition: qgsfeature.h:187
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.
bool disconnect(const QObject *sender, const char *signal, const QObject *receiver, const char *method)
virtual bool isEditable() const override
Returns true if the provider is in editing mode.
const QgsRectangle & filterRect() const
Get the rectangle from which features will be taken.
QString tr(const char *sourceText, const char *disambiguation, int n)
Map canvas is a class for displaying all GIS data types on a canvas.
Definition: qgsmapcanvas.h:109
const QgsAction & at(int idx) const
Get the action at the specified index.
virtual int rowCount(const QModelIndex &parent) const
int x() const
void setView(ViewMode view)
Change the current view mode.
int size() const
bool isNull() const
QgsEditFormConfig * editFormConfig() const
Get the configuration of the form used to represent this vector layer.
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:39
Show a list of the features, where one can be chosen and the according attribute dialog will be prese...
Definition: qgsdualview.h:66
int width() const
virtual QModelIndex index(int row, int column, const QModelIndex &parent=QModelIndex()) const override
void setMode(Mode mode)
Sets the current mode of the form.
int getInt(QWidget *parent, const QString &title, const QString &label, int value, int min, int max, int step, bool *ok, QFlags< Qt::WindowType > flags)
const char * name() const
bool isValid() const
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 setMapping(QObject *sender, int id)
void setSortExpression(const QString &sortExpression, Qt::SortOrder sortOrder=Qt::AscendingOrder)
Set the expression used for sorting the table and feature list.
void processEvents(QFlags< QEventLoop::ProcessEventsFlag > flags)
void append(const T &value)
static QgsExpressionContextScope * globalScope()
Creates a new scope which contains variables and functions relating to the global QGIS context...
FilterMode filterMode()
The current filterModel.
void setChecked(bool checked)
void setLayout(QLayout *layout)
void popup(const QPoint &p, QAction *atAction)
int toInt(bool *ok) const
FilterType
Filter types.
QClipboard * clipboard()
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.
void setEditorContext(const QgsAttributeEditorContext &context)
Sets the context in which this table is shown.
QgsFeatureRequest & disableFilter()
Disables filter conditions.
ViewMode view() const
Returns the current view mode.
Utility class that encapsulates an action based on vector attributes.
Definition: qgsaction.h:25
bool isEmpty() const
Expression contexts are used to encapsulate the parameters around which a QgsExpression should be eva...
bool isEmpty() const
QgsRectangle extent() const
Returns the current zoom exent of the map canvas.
void setCurrentIndex(int index)
This class wraps a request for features to a vector layer (or directly its vector data provider)...
bool runable() const
Checks if the action is runable on the current platform.
Definition: qgsaction.cpp:19
QgsFeatureIds filteredFeatures()
Get a list of currently visible feature ids.
Definition: qgsdualview.h:152
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:97
QgsFeatureRequest & setFlags(const QgsFeatureRequest::Flags &flags)
Set flags that affect how features will be fetched.
void loadFieldNames()
Loads all the field names from the layer.
QGis::GeometryType geometryType() const
Returns point, line or polygon.
Show only features which have unsaved changes.
const QgsFeatureRequest & request() const
Get the the feature request.
QAction * addSeparator()
Encapsulate a field in an attribute table or data source.
Definition: qgsfield.h:44
void hide()
Fast access to features using their ID.
virtual int capabilities() const
Returns a bitmask containing the supported capabilities Note, some capabilities may change depending ...
void addWidget(QWidget *w)
QgsAttributeList pkAttributeList() const
Returns list of attributes making up the primary key.
QRect rect() const
QString displayField() const
Returns the primary display field name used in the identify results dialog.
void setData(const QVariant &userData)
void setExpressionContext(const QgsExpressionContext &context)
Sets the expression context for the widget.
iterator end()
QgsFeatureId id() const
Get the feature ID for this feature.
Definition: qgsfeature.cpp:65
void setWidgetResizable(bool resizable)
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. {.
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 setChecked(bool)
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.
QVariant value(const QString &key, const QVariant &defaultValue) const
QgsSignalBlocker< Object > whileBlocking(Object *object)
Temporarily blocks signals from a QObject while calling a single method from the object.
Definition: qgis.h:333
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.
Qt::SortOrder sortOrder() const
QgsPoint mapToLayerCoordinates(QgsMapLayer *theLayer, QgsPoint point) const
transform point coordinates from output CRS to layer&#39;s CRS
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 setTitle(const QString &title)
void setCheckable(bool checkable)
void setColumnHidden(int column, bool hidden)
Sets whether the specified column should be hidden.
void setWindowTitle(const QString &)
QList< QgsField > toList() const
Utility function to return a list of QgsField instances.
Definition: qgsfield.cpp:466
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.
QString expressionText()
Gets the expression string that has been set in the expression area.
QList< QgsMapLayerAction * > mapLayerActions(QgsMapLayer *layer, const QgsMapLayerAction::Targets &targets=QgsMapLayerAction::AllActions)
Returns the map layer actions which can run on the specified layer.
void formModeChanged(QgsAttributeForm::Mode mode)
Emitted when the form changes mode.
void setExpressionText(const QString &expression)
Sets the expression string for the widget.
bool isValid() const
StandardButton warning(QWidget *parent, const QString &title, const QString &text, QFlags< QMessageBox::StandardButton > buttons, StandardButton defaultButton)
void setText(const QString &text, Mode mode)
void show()
static QgsExpressionContextScope * projectScope()
Creates a new scope which contains variables and functions relating to the current QGIS project...
QgsVectorDataProvider * dataProvider()
Returns the data provider.
void setFilteredFeatures(const QgsFeatureIds &filteredFeatures)
Set a list of currently visible features.
static QgsExpressionContextScope * layerScope(const QgsMapLayer *layer)
Creates a new scope which contains variables and functions relating to a QgsMapLayer.
This is a container for configuration of the attribute table.
Qt::SortOrder sortOrder() const
Get the sort order.
QString widgetType(int fieldIdx) const
Get the id for the editor widget used to represent the field at the given index.
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.
bool connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method, Qt::ConnectionType type)
Is an interface class to abstract feature selection handling.
QObject * parent() const
QString displayExpression() const
Get the preview expression, used to create a human readable preview string.
Represents a vector layer which manages a vector based data sets.
int size() const
Get the number of actions managed by this.
int selectedFeatureCount()
The number of features that are selected in this layer.
QString arg(qlonglong a, int fieldWidth, int base, const QChar &fillChar) const
void setAttributeTableConfig(const QgsAttributeTableConfig &attributeTableConfig)
Set the attribute table configuration object.
iterator begin()
A generic dialog for building expression strings.
void setEnabled(bool)
QgsFeatureRequest & setFilterRect(const QgsRectangle &rect)
Set rectangle from which features will be taken.
int fieldNameIndex(const QString &fieldName) const
Returns the index of a field name or -1 if the field does not exist.
void columnBoxInit()
Initializes widgets which depend on the attributes of this layer.
int featureCount()
Returns the number of features on the layer.