QGIS API Documentation  3.6.0-Noosa (5873452)
qgsattributetablemodel.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  QgsAttributeTableModel.cpp
3  --------------------------------------
4  Date : Feb 2009
5  Copyright : (C) 2009 Vita Cizek
6  Email : weetya (at) gmail.com
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 "qgsattributetablemodel.h"
19 
20 #include "qgsactionmanager.h"
22 #include "qgseditorwidgetfactory.h"
23 #include "qgsexpression.h"
24 #include "qgsfeatureiterator.h"
25 #include "qgsconditionalstyle.h"
26 #include "qgsfields.h"
27 #include "qgsfieldformatter.h"
28 #include "qgslogger.h"
29 #include "qgsmapcanvas.h"
31 #include "qgsrenderer.h"
32 #include "qgsvectorlayer.h"
33 #include "qgsvectordataprovider.h"
34 #include "qgssymbollayerutils.h"
36 #include "qgsgui.h"
37 #include "qgsexpressionnodeimpl.h"
38 #include "qgsvectorlayerjoininfo.h"
40 #include "qgsfieldmodel.h"
43 
44 #include <QVariant>
45 
46 #include <limits>
47 
49  : QAbstractTableModel( parent )
50  , mLayerCache( layerCache )
51 {
52  mExpressionContext.appendScopes( QgsExpressionContextUtils::globalProjectLayerScopes( layerCache->layer() ) );
53 
54  if ( layerCache->layer()->geometryType() == QgsWkbTypes::NullGeometry )
55  {
56  mFeatureRequest.setFlags( QgsFeatureRequest::NoGeometry );
57  }
58 
59  mFeat.setId( std::numeric_limits<int>::min() );
60 
61  if ( !layer()->isSpatial() )
62  mFeatureRequest.setFlags( QgsFeatureRequest::NoGeometry );
63 
64  loadAttributes();
65 
66  connect( layer(), &QgsVectorLayer::featuresDeleted, this, &QgsAttributeTableModel::featuresDeleted );
67  connect( layer(), &QgsVectorLayer::attributeDeleted, this, &QgsAttributeTableModel::attributeDeleted );
68  connect( layer(), &QgsVectorLayer::updatedFields, this, &QgsAttributeTableModel::updatedFields );
69 
70  connect( layer(), &QgsVectorLayer::editCommandStarted, this, &QgsAttributeTableModel::bulkEditCommandStarted );
71  connect( layer(), &QgsVectorLayer::beforeRollBack, this, &QgsAttributeTableModel::bulkEditCommandStarted );
72  connect( layer(), &QgsVectorLayer::afterRollBack, this, &QgsAttributeTableModel::bulkEditCommandEnded );
73 
74  connect( layer(), &QgsVectorLayer::editCommandEnded, this, &QgsAttributeTableModel::editCommandEnded );
75  connect( mLayerCache, &QgsVectorLayerCache::attributeValueChanged, this, &QgsAttributeTableModel::attributeValueChanged );
76  connect( mLayerCache, &QgsVectorLayerCache::featureAdded, this, [ = ]( QgsFeatureId id ) { featureAdded( id ); } );
77  connect( mLayerCache, &QgsVectorLayerCache::cachedLayerDeleted, this, &QgsAttributeTableModel::layerDeleted );
78 
79 }
80 
81 bool QgsAttributeTableModel::loadFeatureAtId( QgsFeatureId fid ) const
82 {
83  QgsDebugMsgLevel( QStringLiteral( "loading feature %1" ).arg( fid ), 3 );
84 
85  if ( fid == std::numeric_limits<int>::min() )
86  {
87  return false;
88  }
89 
90  return mLayerCache->featureAtId( fid, mFeat );
91 }
92 
94 {
95  return mExtraColumns;
96 }
97 
99 {
100  mExtraColumns = extraColumns;
101  loadAttributes();
102 }
103 
104 void QgsAttributeTableModel::featuresDeleted( const QgsFeatureIds &fids )
105 {
106  QList<int> rows;
107 
108  Q_FOREACH ( QgsFeatureId fid, fids )
109  {
110  QgsDebugMsgLevel( QStringLiteral( "(%2) fid: %1, size: %3" ).arg( fid ).arg( mFeatureRequest.filterType() ).arg( mIdRowMap.size() ), 4 );
111 
112  int row = idToRow( fid );
113  if ( row != -1 )
114  rows << row;
115  }
116 
117  std::sort( rows.begin(), rows.end() );
118 
119  int lastRow = -1;
120  int beginRow = -1;
121  int currentRowCount = 0;
122  int removedRows = 0;
123  bool reset = false;
124 
125  Q_FOREACH ( int row, rows )
126  {
127 #if 0
128  qDebug() << "Row: " << row << ", begin " << beginRow << ", last " << lastRow << ", current " << currentRowCount << ", removed " << removedRows;
129 #endif
130  if ( lastRow == -1 )
131  {
132  beginRow = row;
133  }
134 
135  if ( row != lastRow + 1 && lastRow != -1 )
136  {
137  if ( rows.count() > 100 && currentRowCount < 10 )
138  {
139  reset = true;
140  break;
141  }
142  removeRows( beginRow - removedRows, currentRowCount );
143 
144  beginRow = row;
145  removedRows += currentRowCount;
146  currentRowCount = 0;
147  }
148 
149  currentRowCount++;
150 
151  lastRow = row;
152  }
153 
154  if ( !reset )
155  removeRows( beginRow - removedRows, currentRowCount );
156  else
157  resetModel();
158 }
159 
160 bool QgsAttributeTableModel::removeRows( int row, int count, const QModelIndex &parent )
161 {
162 
163  if ( row < 0 || count < 1 )
164  return false;
165 
166  beginRemoveRows( parent, row, row + count - 1 );
167 
168 #ifdef QGISDEBUG
169  if ( 3 <= QgsLogger::debugLevel() )
170  QgsDebugMsgLevel( QStringLiteral( "remove %2 rows at %1 (rows %3, ids %4)" ).arg( row ).arg( count ).arg( mRowIdMap.size() ).arg( mIdRowMap.size() ), 3 );
171 #endif
172 
173  // clean old references
174  for ( int i = row; i < row + count; i++ )
175  {
176  for ( SortCache &cache : mSortCaches )
177  cache.sortCache.remove( mRowIdMap[i] );
178  mIdRowMap.remove( mRowIdMap[i] );
179  mRowIdMap.remove( i );
180  }
181 
182  // update maps
183  int n = mRowIdMap.size() + count;
184  for ( int i = row + count; i < n; i++ )
185  {
186  QgsFeatureId id = mRowIdMap[i];
187  mIdRowMap[id] -= count;
188  mRowIdMap[i - count] = id;
189  mRowIdMap.remove( i );
190  }
191 
192 #ifdef QGISDEBUG
193  if ( 4 <= QgsLogger::debugLevel() )
194  {
195  QgsDebugMsgLevel( QStringLiteral( "after removal rows %1, ids %2" ).arg( mRowIdMap.size() ).arg( mIdRowMap.size() ), 4 );
196  QgsDebugMsgLevel( QStringLiteral( "id->row" ), 4 );
197  for ( QHash<QgsFeatureId, int>::const_iterator it = mIdRowMap.constBegin(); it != mIdRowMap.constEnd(); ++it )
198  QgsDebugMsgLevel( QStringLiteral( "%1->%2" ).arg( FID_TO_STRING( it.key() ) ).arg( *it ), 4 );
199 
200  QgsDebugMsgLevel( QStringLiteral( "row->id" ), 4 );
201  for ( QHash<int, QgsFeatureId>::const_iterator it = mRowIdMap.constBegin(); it != mRowIdMap.constEnd(); ++it )
202  QgsDebugMsgLevel( QStringLiteral( "%1->%2" ).arg( it.key() ).arg( FID_TO_STRING( *it ) ), 4 );
203  }
204 #endif
205 
206  Q_ASSERT( mRowIdMap.size() == mIdRowMap.size() );
207 
208  endRemoveRows();
209 
210  return true;
211 }
212 
213 void QgsAttributeTableModel::featureAdded( QgsFeatureId fid, bool resettingModel )
214 {
215  QgsDebugMsgLevel( QStringLiteral( "(%2) fid: %1" ).arg( fid ).arg( mFeatureRequest.filterType() ), 4 );
216  bool featOk = true;
217 
218  if ( mFeat.id() != fid )
219  featOk = loadFeatureAtId( fid );
220 
221  if ( featOk && mFeatureRequest.acceptFeature( mFeat ) )
222  {
223  for ( SortCache &cache : mSortCaches )
224  {
225  if ( cache.sortFieldIndex >= 0 )
226  {
227  QgsFieldFormatter *fieldFormatter = mFieldFormatters.at( cache.sortFieldIndex );
228  const QVariant &widgetCache = mAttributeWidgetCaches.at( cache.sortFieldIndex );
229  const QVariantMap &widgetConfig = mWidgetConfigs.at( cache.sortFieldIndex );
230  QVariant sortValue = fieldFormatter->representValue( layer(), cache.sortFieldIndex, widgetConfig, widgetCache, mFeat.attribute( cache.sortFieldIndex ) );
231  cache.sortCache.insert( mFeat.id(), sortValue );
232  }
233  else if ( cache.sortCacheExpression.isValid() )
234  {
235  mExpressionContext.setFeature( mFeat );
236  cache.sortCache[mFeat.id()] = cache.sortCacheExpression.evaluate( &mExpressionContext );
237  }
238  }
239 
240  // Skip if the fid is already in the map (do not add twice)!
241  if ( ! mIdRowMap.contains( fid ) )
242  {
243  int n = mRowIdMap.size();
244  if ( !resettingModel )
245  beginInsertRows( QModelIndex(), n, n );
246  mIdRowMap.insert( fid, n );
247  mRowIdMap.insert( n, fid );
248  if ( !resettingModel )
249  endInsertRows();
250  reload( index( rowCount() - 1, 0 ), index( rowCount() - 1, columnCount() ) );
251  }
252  }
253 }
254 
255 void QgsAttributeTableModel::updatedFields()
256 {
257  loadAttributes();
258  emit modelChanged();
259 }
260 
261 void QgsAttributeTableModel::editCommandEnded()
262 {
263  // do not do reload(...) due would trigger (dataChanged) row sort
264  // giving issue: https://issues.qgis.org/issues/15976
265  bulkEditCommandEnded( );
266 }
267 
268 void QgsAttributeTableModel::attributeDeleted( int idx )
269 {
270  int cacheIndex = 0;
271  for ( const SortCache &cache : mSortCaches )
272  {
273  if ( cache.sortCacheAttributes.contains( idx ) )
274  {
275  prefetchSortData( QString(), cacheIndex );
276  }
277  cacheIndex++;
278  }
279 }
280 
281 void QgsAttributeTableModel::layerDeleted()
282 {
283  mLayerCache = nullptr;
284  removeRows( 0, rowCount() );
285 
286  mAttributeWidgetCaches.clear();
287  mAttributes.clear();
288  mWidgetFactories.clear();
289  mWidgetConfigs.clear();
290  mFieldFormatters.clear();
291 }
292 
293 void QgsAttributeTableModel::fieldFormatterRemoved( QgsFieldFormatter *fieldFormatter )
294 {
295  for ( int i = 0; i < mFieldFormatters.size(); ++i )
296  {
297  if ( mFieldFormatters.at( i ) == fieldFormatter )
299  }
300 }
301 
302 void QgsAttributeTableModel::attributeValueChanged( QgsFeatureId fid, int idx, const QVariant &value )
303 {
304  // Defer all updates if a bulk edit/rollback command is running
305  if ( mBulkEditCommandRunning )
306  {
307  mAttributeValueChanges.insert( QPair<QgsFeatureId, int>( fid, idx ), value );
308  return;
309  }
310  QgsDebugMsgLevel( QStringLiteral( "(%4) fid: %1, idx: %2, value: %3" ).arg( fid ).arg( idx ).arg( value.toString() ).arg( mFeatureRequest.filterType() ), 2 );
311 
312  for ( SortCache &cache : mSortCaches )
313  {
314  if ( cache.sortCacheAttributes.contains( idx ) )
315  {
316  if ( cache.sortFieldIndex == -1 )
317  {
318  if ( loadFeatureAtId( fid ) )
319  {
320  mExpressionContext.setFeature( mFeat );
321  cache.sortCache[fid] = cache.sortCacheExpression.evaluate( &mExpressionContext );
322  }
323  }
324  else
325  {
326  QgsFieldFormatter *fieldFormatter = mFieldFormatters.at( cache.sortFieldIndex );
327  const QVariant &widgetCache = mAttributeWidgetCaches.at( cache.sortFieldIndex );
328  const QVariantMap &widgetConfig = mWidgetConfigs.at( cache.sortFieldIndex );
329  QVariant sortValue = fieldFormatter->representValue( layer(), cache.sortFieldIndex, widgetConfig, widgetCache, value );
330  cache.sortCache.insert( fid, sortValue );
331  }
332  }
333  }
334  // No filter request: skip all possibly heavy checks
335  if ( mFeatureRequest.filterType() == QgsFeatureRequest::FilterNone )
336  {
337  if ( loadFeatureAtId( fid ) )
338  setData( index( idToRow( fid ), fieldCol( idx ) ), value, Qt::EditRole );
339  }
340  else
341  {
342  if ( loadFeatureAtId( fid ) )
343  {
344  if ( mFeatureRequest.acceptFeature( mFeat ) )
345  {
346  if ( !mIdRowMap.contains( fid ) )
347  {
348  // Feature changed in such a way, it will be shown now
349  featureAdded( fid );
350  }
351  else
352  {
353  // Update representation
354  setData( index( idToRow( fid ), fieldCol( idx ) ), value, Qt::EditRole );
355  }
356  }
357  else
358  {
359  if ( mIdRowMap.contains( fid ) )
360  {
361  // Feature changed such, that it is no longer shown
362  featuresDeleted( QgsFeatureIds() << fid );
363  }
364  // else: we don't care
365  }
366  }
367  }
368 }
369 
370 void QgsAttributeTableModel::loadAttributes()
371 {
372  if ( !layer() )
373  {
374  return;
375  }
376 
377  bool ins = false, rm = false;
378 
379  QgsAttributeList attributes;
380  const QgsFields &fields = layer()->fields();
381 
382  mWidgetFactories.clear();
383  mAttributeWidgetCaches.clear();
384  mWidgetConfigs.clear();
385 
386  for ( int idx = 0; idx < fields.count(); ++idx )
387  {
388  const QgsEditorWidgetSetup setup = QgsGui::editorWidgetRegistry()->findBest( layer(), fields[idx].name() );
389  QgsEditorWidgetFactory *widgetFactory = QgsGui::editorWidgetRegistry()->factory( setup.type() );
391 
392  mWidgetFactories.append( widgetFactory );
393  mWidgetConfigs.append( setup.config() );
394  mAttributeWidgetCaches.append( fieldFormatter->createCache( layer(), idx, setup.config() ) );
395  mFieldFormatters.append( fieldFormatter );
396 
397  attributes << idx;
398  }
399 
400  if ( mFieldCount + mExtraColumns < attributes.size() + mExtraColumns )
401  {
402  ins = true;
403  beginInsertColumns( QModelIndex(), mFieldCount + mExtraColumns, attributes.size() - 1 );
404  }
405  else if ( attributes.size() + mExtraColumns < mFieldCount + mExtraColumns )
406  {
407  rm = true;
408  beginRemoveColumns( QModelIndex(), attributes.size(), mFieldCount + mExtraColumns - 1 );
409  }
410 
411  mFieldCount = attributes.size();
412  mAttributes = attributes;
413 
414  for ( SortCache &cache : mSortCaches )
415  {
416  if ( cache.sortFieldIndex >= mAttributes.count() )
417  cache.sortFieldIndex = -1;
418  }
419 
420  if ( ins )
421  {
422  endInsertColumns();
423  }
424  else if ( rm )
425  {
426  endRemoveColumns();
427  }
428 }
429 
431 {
432  // make sure attributes are properly updated before caching the data
433  // (emit of progress() signal may enter event loop and thus attribute
434  // table view may be updated with inconsistent model which may assume
435  // wrong number of attributes)
436  loadAttributes();
437 
438  beginResetModel();
439 
440  if ( rowCount() != 0 )
441  {
442  removeRows( 0, rowCount() );
443  }
444 
445  // Layer might have been deleted and cache set to nullptr!
446  if ( mLayerCache )
447  {
448  QgsFeatureIterator features = mLayerCache->getFeatures( mFeatureRequest );
449 
450  int i = 0;
451 
452  QTime t;
453  t.start();
454 
455  while ( features.nextFeature( mFeat ) )
456  {
457  ++i;
458 
459  if ( t.elapsed() > 1000 )
460  {
461  bool cancel = false;
462  emit progress( i, cancel );
463  if ( cancel )
464  break;
465 
466  t.restart();
467  }
468  featureAdded( mFeat.id(), true );
469  }
470 
471  emit finished();
472  connect( mLayerCache, &QgsVectorLayerCache::invalidated, this, &QgsAttributeTableModel::loadLayer, Qt::UniqueConnection );
473  }
474 
475  endResetModel();
476 }
477 
478 
480 {
481  if ( fieldName.isNull() )
482  {
483  mRowStylesMap.clear();
484  emit dataChanged( index( 0, 0 ), index( rowCount() - 1, columnCount() - 1 ) );
485  return;
486  }
487 
488  int fieldIndex = mLayerCache->layer()->fields().lookupField( fieldName );
489  if ( fieldIndex == -1 )
490  return;
491 
492  //whole column has changed
493  int col = fieldCol( fieldIndex );
494  emit dataChanged( index( 0, col ), index( rowCount() - 1, col ) );
495 }
496 
498 {
499  if ( a == b )
500  return;
501 
502  int rowA = idToRow( a );
503  int rowB = idToRow( b );
504 
505  //emit layoutAboutToBeChanged();
506 
507  mRowIdMap.remove( rowA );
508  mRowIdMap.remove( rowB );
509  mRowIdMap.insert( rowA, b );
510  mRowIdMap.insert( rowB, a );
511 
512  mIdRowMap.remove( a );
513  mIdRowMap.remove( b );
514  mIdRowMap.insert( a, rowB );
515  mIdRowMap.insert( b, rowA );
516  Q_ASSERT( mRowIdMap.size() == mIdRowMap.size() );
517 
518 
519  //emit layoutChanged();
520 }
521 
523 {
524  if ( !mIdRowMap.contains( id ) )
525  {
526  QgsDebugMsg( QStringLiteral( "idToRow: id %1 not in the map" ).arg( id ) );
527  return -1;
528  }
529 
530  return mIdRowMap[id];
531 }
532 
534 {
535  return index( idToRow( id ), 0 );
536 }
537 
539 {
540  QModelIndexList indexes;
541 
542  int row = idToRow( id );
543  int columns = columnCount();
544  indexes.reserve( columns );
545  for ( int column = 0; column < columns; ++column )
546  {
547  indexes.append( index( row, column ) );
548  }
549 
550  return indexes;
551 }
552 
554 {
555  if ( !mRowIdMap.contains( row ) )
556  {
557  QgsDebugMsg( QStringLiteral( "rowToId: row %1 not in the map" ).arg( row ) );
558  // return negative infinite (to avoid collision with newly added features)
559  return std::numeric_limits<int>::min();
560  }
561 
562  return mRowIdMap[row];
563 }
564 
566 {
567  return mAttributes[col];
568 }
569 
571 {
572  return mAttributes.indexOf( idx );
573 }
574 
575 int QgsAttributeTableModel::rowCount( const QModelIndex &parent ) const
576 {
577  Q_UNUSED( parent );
578  return mRowIdMap.size();
579 }
580 
581 int QgsAttributeTableModel::columnCount( const QModelIndex &parent ) const
582 {
583  Q_UNUSED( parent );
584  return std::max( 1, mFieldCount + mExtraColumns ); // if there are zero columns all model indices will be considered invalid
585 }
586 
587 QVariant QgsAttributeTableModel::headerData( int section, Qt::Orientation orientation, int role ) const
588 {
589  if ( !layer() )
590  return QVariant();
591 
592  if ( role == Qt::DisplayRole )
593  {
594  if ( orientation == Qt::Vertical ) //row
595  {
596  return QVariant( section );
597  }
598  else if ( section >= 0 && section < mFieldCount )
599  {
600  QString attributeName = layer()->fields().at( mAttributes.at( section ) ).displayName();
601  return QVariant( attributeName );
602  }
603  else
604  {
605  return tr( "extra column" );
606  }
607  }
608  else if ( role == Qt::ToolTipRole )
609  {
610  if ( orientation == Qt::Vertical )
611  {
612  // TODO show DisplayExpression
613  return tr( "Feature ID: %1" ).arg( rowToId( section ) );
614  }
615  else
616  {
617  const QgsField field = layer()->fields().at( mAttributes.at( section ) );
618  return QgsFieldModel::fieldToolTip( field );
619  }
620  }
621  else
622  {
623  return QVariant();
624  }
625 }
626 
627 QVariant QgsAttributeTableModel::data( const QModelIndex &index, int role ) const
628 {
629  if ( !index.isValid() || !layer() ||
630  ( role != Qt::TextAlignmentRole
631  && role != Qt::DisplayRole
632  && role != Qt::ToolTipRole
633  && role != Qt::EditRole
634  && role != FeatureIdRole
635  && role != FieldIndexRole
636  && role != Qt::BackgroundColorRole
637  && role != Qt::TextColorRole
638  && role != Qt::DecorationRole
639  && role != Qt::FontRole
640  && role < SortRole
641  )
642  )
643  return QVariant();
644 
645  QgsFeatureId rowId = rowToId( index.row() );
646 
647  if ( role == FeatureIdRole )
648  return rowId;
649 
650  if ( index.column() >= mFieldCount )
651  return QVariant();
652 
653  int fieldId = mAttributes.at( index.column() );
654 
655  if ( role == FieldIndexRole )
656  return fieldId;
657 
658  if ( role >= SortRole )
659  {
660  unsigned long cacheIndex = role - SortRole;
661  if ( cacheIndex < mSortCaches.size() )
662  return mSortCaches.at( cacheIndex ).sortCache.value( rowId );
663  else
664  return QVariant();
665  }
666 
667  QgsField field = layer()->fields().at( fieldId );
668 
669  if ( role == Qt::TextAlignmentRole )
670  {
671  return QVariant( mFieldFormatters.at( index.column() )->alignmentFlag( layer(), fieldId, mWidgetConfigs.at( index.column() ) ) | Qt::AlignVCenter );
672  }
673 
674  if ( mFeat.id() != rowId || !mFeat.isValid() )
675  {
676  if ( !loadFeatureAtId( rowId ) )
677  return QVariant( "ERROR" );
678 
679  if ( mFeat.id() != rowId )
680  return QVariant( "ERROR" );
681  }
682 
683  QVariant val = mFeat.attribute( fieldId );
684 
685  switch ( role )
686  {
687  case Qt::DisplayRole:
688  case Qt::ToolTipRole:
689  return mFieldFormatters.at( index.column() )->representValue( layer(), fieldId, mWidgetConfigs.at( index.column() ),
690  mAttributeWidgetCaches.at( index.column() ), val );
691 
692  case Qt::EditRole:
693  return val;
694 
695  case Qt::BackgroundRole:
696  case Qt::TextColorRole:
697  case Qt::DecorationRole:
698  case Qt::FontRole:
699  {
700  mExpressionContext.setFeature( mFeat );
701  QList<QgsConditionalStyle> styles;
702  if ( mRowStylesMap.contains( index.row() ) )
703  {
704  styles = mRowStylesMap[index.row()];
705  }
706  else
707  {
708  styles = QgsConditionalStyle::matchingConditionalStyles( layer()->conditionalStyles()->rowStyles(), QVariant(), mExpressionContext );
709  mRowStylesMap.insert( index.row(), styles );
710  }
711 
713  styles = layer()->conditionalStyles()->fieldStyles( field.name() );
714  styles = QgsConditionalStyle::matchingConditionalStyles( styles, val, mExpressionContext );
715  styles.insert( 0, rowstyle );
717 
718  if ( style.isValid() )
719  {
720  if ( role == Qt::BackgroundRole && style.validBackgroundColor() )
721  return style.backgroundColor();
722  if ( role == Qt::TextColorRole && style.validTextColor() )
723  return style.textColor();
724  if ( role == Qt::DecorationRole )
725  return style.icon();
726  if ( role == Qt::FontRole )
727  return style.font();
728  }
729 
730  return QVariant();
731  }
732  }
733 
734  return QVariant();
735 }
736 
737 bool QgsAttributeTableModel::setData( const QModelIndex &index, const QVariant &value, int role )
738 {
739  Q_UNUSED( value )
740 
741  if ( !index.isValid() || index.column() >= mFieldCount || role != Qt::EditRole || !layer()->isEditable() )
742  return false;
743 
744  if ( !layer()->isModified() )
745  return false;
746 
747  mRowStylesMap.remove( index.row() );
748 
749  return true;
750 }
751 
752 Qt::ItemFlags QgsAttributeTableModel::flags( const QModelIndex &index ) const
753 {
754  if ( !index.isValid() )
755  return Qt::ItemIsEnabled;
756 
757  if ( index.column() >= mFieldCount || !layer() )
758  return Qt::NoItemFlags;
759 
760  Qt::ItemFlags flags = QAbstractTableModel::flags( index );
761 
762  bool editable = false;
763  const int fieldIndex = mAttributes[index.column()];
764  const QgsFeatureId fid = rowToId( index.row() );
765  if ( layer()->fields().fieldOrigin( fieldIndex ) == QgsFields::OriginJoin )
766  {
767  int srcFieldIndex;
768  const QgsVectorLayerJoinInfo *info = layer()->joinBuffer()->joinForFieldIndex( fieldIndex, layer()->fields(), srcFieldIndex );
769 
770  if ( info && info->isEditable() )
771  editable = fieldIsEditable( *info->joinLayer(), srcFieldIndex, fid );
772  }
773  else
774  editable = fieldIsEditable( *layer(), fieldIndex, fid );
775 
776  if ( editable )
777  flags |= Qt::ItemIsEditable;
778 
779  return flags;
780 }
781 
782 bool QgsAttributeTableModel::fieldIsEditable( const QgsVectorLayer &layer, int fieldIndex, QgsFeatureId fid ) const
783 {
784  return ( layer.isEditable() &&
785  !layer.editFormConfig().readOnly( fieldIndex ) &&
787 }
788 
789 void QgsAttributeTableModel::bulkEditCommandStarted()
790 {
791  mBulkEditCommandRunning = true;
792  mAttributeValueChanges.clear();
793 }
794 
795 void QgsAttributeTableModel::bulkEditCommandEnded()
796 {
797  mBulkEditCommandRunning = false;
798  // Full model update if the changed rows are more than half the total rows
799  // or if their count is > layer cache size
800  int changeCount( mAttributeValueChanges.count() );
801  bool fullModelUpdate = changeCount > mLayerCache->cacheSize() ||
802  changeCount > rowCount() * 0.5;
803 
804  QgsDebugMsgLevel( QStringLiteral( "Bulk edit command ended with %1 modified rows over (%4), cache size is %2, starting %3 update." )
805  .arg( changeCount )
806  .arg( mLayerCache->cacheSize() )
807  .arg( fullModelUpdate ? QStringLiteral( "full" ) : QStringLiteral( "incremental" ) )
808  .arg( rowCount() ),
809  3 );
810  // Invalidates the whole model
811  if ( fullModelUpdate )
812  {
813  // Invalidates the cache (there is no API for doing this directly)
814  emit mLayerCache->layer()->dataChanged();
815  emit dataChanged( createIndex( 0, 0 ), createIndex( rowCount() - 1, columnCount() - 1 ) );
816  }
817  else
818  {
819  int minRow = rowCount();
820  int minCol = columnCount();
821  int maxRow = 0;
822  int maxCol = 0;
823  const auto keys = mAttributeValueChanges.keys();
824  for ( const auto &key : keys )
825  {
826  attributeValueChanged( key.first, key.second, mAttributeValueChanges.value( key ) );
827  int row( idToRow( key.first ) );
828  int col( fieldCol( key.second ) );
829  minRow = std::min<int>( row, minRow );
830  minCol = std::min<int>( col, minCol );
831  maxRow = std::max<int>( row, maxRow );
832  maxCol = std::max<int>( col, maxCol );
833  }
834  emit dataChanged( createIndex( minRow, minCol ), createIndex( maxRow, maxCol ) );
835  }
836  mAttributeValueChanges.clear();
837 }
838 
839 void QgsAttributeTableModel::reload( const QModelIndex &index1, const QModelIndex &index2 )
840 {
841  mFeat.setId( std::numeric_limits<int>::min() );
842  emit dataChanged( index1, index2 );
843 }
844 
845 
846 void QgsAttributeTableModel::executeAction( QUuid action, const QModelIndex &idx ) const
847 {
848  QgsFeature f = feature( idx );
849  layer()->actions()->doAction( action, f, fieldIdx( idx.column() ) );
850 }
851 
852 void QgsAttributeTableModel::executeMapLayerAction( QgsMapLayerAction *action, const QModelIndex &idx ) const
853 {
854  QgsFeature f = feature( idx );
855  action->triggerForFeature( layer(), &f );
856 }
857 
858 QgsFeature QgsAttributeTableModel::feature( const QModelIndex &idx ) const
859 {
860  QgsFeature f;
861  f.initAttributes( mAttributes.size() );
862  f.setId( rowToId( idx.row() ) );
863  for ( int i = 0; i < mAttributes.size(); i++ )
864  {
865  f.setAttribute( mAttributes[i], data( index( idx.row(), i ), Qt::EditRole ) );
866  }
867 
868  return f;
869 }
870 
872 {
873  if ( column == -1 || column >= mAttributes.count() )
874  {
875  prefetchSortData( QString() );
876  }
877  else
878  {
879  prefetchSortData( QgsExpression::quotedColumnRef( mLayerCache->layer()->fields().at( mAttributes.at( column ) ).name() ) );
880  }
881 }
882 
883 void QgsAttributeTableModel::prefetchSortData( const QString &expressionString, unsigned long cacheIndex )
884 {
885  if ( cacheIndex >= mSortCaches.size() )
886  {
887  mSortCaches.resize( cacheIndex + 1 );
888  }
889  SortCache &cache = mSortCaches[cacheIndex];
890  cache.sortCache.clear();
891  cache.sortCacheAttributes.clear();
892  cache.sortFieldIndex = -1;
893  if ( !expressionString.isEmpty() )
894  cache.sortCacheExpression = QgsExpression( expressionString );
895  else
896  {
897  // no sorting
898  cache.sortCacheExpression = QgsExpression();
899  return;
900  }
901 
902  QgsFieldFormatter *fieldFormatter = nullptr;
903  QVariant widgetCache;
904  QVariantMap widgetConfig;
905 
906  if ( cache.sortCacheExpression.isField() )
907  {
908  QString fieldName = static_cast<const QgsExpressionNodeColumnRef *>( cache.sortCacheExpression.rootNode() )->name();
909  cache.sortFieldIndex = mLayerCache->layer()->fields().lookupField( fieldName );
910  }
911 
912  if ( cache.sortFieldIndex == -1 )
913  {
914  cache.sortCacheExpression.prepare( &mExpressionContext );
915 
916  const QSet<QString> &referencedColumns = cache.sortCacheExpression.referencedColumns();
917 
918  for ( const QString &col : referencedColumns )
919  {
920  cache.sortCacheAttributes.append( mLayerCache->layer()->fields().lookupField( col ) );
921  }
922  }
923  else
924  {
925  cache.sortCacheAttributes.append( cache.sortFieldIndex );
926 
927  widgetCache = mAttributeWidgetCaches.at( cache.sortFieldIndex );
928  widgetConfig = mWidgetConfigs.at( cache.sortFieldIndex );
929  fieldFormatter = mFieldFormatters.at( cache.sortFieldIndex );
930  }
931 
932  QgsFeatureRequest request = QgsFeatureRequest( mFeatureRequest )
934  .setSubsetOfAttributes( cache.sortCacheAttributes );
935  QgsFeatureIterator it = mLayerCache->getFeatures( request );
936 
937  QgsFeature f;
938  while ( it.nextFeature( f ) )
939  {
940  if ( cache.sortFieldIndex == -1 )
941  {
942  mExpressionContext.setFeature( f );
943  const QVariant cacheValue = cache.sortCacheExpression.evaluate( &mExpressionContext );
944  cache.sortCache.insert( f.id(), cacheValue );
945  }
946  else
947  {
948  QVariant sortValue = fieldFormatter->sortValue( layer(), cache.sortFieldIndex, widgetConfig, widgetCache, f.attribute( cache.sortFieldIndex ) );
949  cache.sortCache.insert( f.id(), sortValue );
950  }
951  }
952 }
953 
954 QString QgsAttributeTableModel::sortCacheExpression( unsigned long cacheIndex ) const
955 {
956  QString expressionString;
957 
958  if ( cacheIndex >= mSortCaches.size() )
959  return expressionString;
960 
961  const QgsExpression &expression = mSortCaches[cacheIndex].sortCacheExpression;
962 
963  if ( expression.isValid() )
964  expressionString = expression.expression();
965  else
966  expressionString = QString();
967 
968  return expressionString;
969 }
970 
972 {
973  mFeatureRequest = request;
974  if ( layer() && !layer()->isSpatial() )
975  mFeatureRequest.setFlags( mFeatureRequest.flags() | QgsFeatureRequest::NoGeometry );
976 }
977 
979 {
980  return mFeatureRequest;
981 }
int lookupField(const QString &fieldName) const
Looks up field&#39;s index from the field name.
Definition: qgsfields.cpp:324
bool isValid() const
Returns the validity of this feature.
Definition: qgsfeature.cpp:183
void setRequest(const QgsFeatureRequest &request)
Set a request that will be used to fill this attribute table model.
Class for parsing and evaluation of expressions (formerly called "search strings").
QgsActionManager * actions()
Returns all layer actions defined on this layer.
QgsFeatureId id
Definition: qgsfeature.h:64
Wrapper for iterator of features from vector data provider or vector layer.
void featuresDeleted(const QgsFeatureIds &fids)
Emitted when features have been deleted.
QgsVectorLayer * layer() const
Returns the layer this model uses as backend.
int extraColumns() const
Empty extra columns to announce from this model.
bool acceptFeature(const QgsFeature &feature)
Check if a feature is accepted by this requests filter.
void doAction(QUuid actionId, const QgsFeature &feature, int defaultValueIndex=0, const QgsExpressionContextScope &scope=QgsExpressionContextScope())
Does the given action.
virtual void loadLayer()
Loads the layer into the model Preferably to be called, before using this model as source for any oth...
Field comes from a joined layer (originIndex / 1000 = index of the join, originIndex % 1000 = index w...
Definition: qgsfields.h:50
QSet< QgsFeatureId > QgsFeatureIds
Definition: qgsfeatureid.h:34
QVariantMap config() const
static QgsFieldFormatterRegistry * fieldFormatterRegistry()
Gets the registry of available field formatters.
FieldOrigin fieldOrigin(int fieldIdx) const
Gets field&#39;s origin (value from an enumeration)
Definition: qgsfields.cpp:189
virtual QgsVectorDataProvider::Capabilities capabilities() const
Returns flags containing the supported capabilities.
static QString quotedColumnRef(QString name)
Returns a quoted column reference (in double quotes)
const Flags & flags() const
QString name
Definition: qgsfield.h:58
Get the field index of this column.
void beforeRollBack()
Is emitted, before changes are rolled back.
void invalidated()
The cache has been invalidated and cleared.
QgsEditorWidgetFactory * factory(const QString &widgetId)
Gets a factory for the given widget type id.
virtual QVariant createCache(QgsVectorLayer *layer, int fieldIndex, const QVariantMap &config) const
Create a cache for a given field.
void setFeature(const QgsFeature &feature)
Convenience function for setting a feature for the context.
#define QgsDebugMsg(str)
Definition: qgslogger.h:38
QString sortCacheExpression(unsigned long cacheIndex=0) const
The expression which was used to fill the sorting cache at index cacheIndex.
QList< QgsConditionalStyle > fieldStyles(const QString &fieldName)
Returns the conditional styles set for the field UI properties.
void prefetchSortData(const QString &expression, unsigned long cacheIndex=0)
Prefetches the entire data for an expression.
static QString fieldToolTip(const QgsField &field)
Returns a HTML formatted tooltip string for a field, containing details like the field name...
void reload(const QModelIndex &index1, const QModelIndex &index2)
Reloads the model data between indices.
qint64 QgsFeatureId
Definition: qgsfeatureid.h:25
QgsFeatureRequest & setSubsetOfAttributes(const QgsAttributeList &attrs)
Set a subset of attributes that will be fetched.
FilterType filterType() const
Returns the filter type which is currently set on this request.
virtual QVariant sortValue(QgsVectorLayer *layer, int fieldIndex, const QVariantMap &config, const QVariant &cache, const QVariant &value) const
If the default sort order should be overwritten for this widget, you can transform the value in here...
QgsWkbTypes::GeometryType geometryType() const
Returns point, line or polygon.
Container of fields for a vector layer.
Definition: qgsfields.h:42
bool setAttribute(int field, const QVariant &attr)
Set an attribute&#39;s value by field index.
Definition: qgsfeature.cpp:211
bool validBackgroundColor() const
Check if the background color is valid for render.
QPixmap icon() const
The icon set for style generated from the set symbol.
void fieldConditionalStyleChanged(const QString &fieldName)
Handles updating the model when the conditional style for a field changes.
QgsVectorLayer * layer()
Returns the layer to which this cache belongs.
The feature class encapsulates a single feature including its id, geometry and a list of field/values...
Definition: qgsfeature.h:55
void attributeValueChanged(QgsFeatureId fid, int field, const QVariant &value)
Is emitted when an attribute is changed.
int count() const
Returns number of items.
Definition: qgsfields.cpp:133
void editCommandEnded()
Signal emitted, when an edit command successfully ended.
void resetModel()
Resets the model.
QModelIndex idToIndex(QgsFeatureId id) const
QgsConditionalLayerStyles * conditionalStyles() const
Returns the conditional styles that are set for this layer.
QgsField at(int i) const
Gets field at particular index (must be in range 0..N-1)
Definition: qgsfields.cpp:163
QVariant data(const QModelIndex &index, int role) const override
Returns data on the given index.
Get the feature id of the feature in this row.
QgsFeature feature(const QModelIndex &idx) const
Returns the feature attributes at given model index.
void featureAdded(QgsFeatureId fid)
Is emitted, when a new feature has been added to the layer and this cache.
bool isEditable() const FINAL
Returns true if the provider is in editing mode.
Roles used for sorting start here.
QVariant headerData(int section, Qt::Orientation orientation, int role=Qt::DisplayRole) const override
Returns header data.
QgsFields fields() const FINAL
Returns the list of fields of this layer.
Conditional styling for a rule.
QgsVectorLayer * joinLayer() const
Returns joined layer (may be null if the reference was set by layer ID and not resolved yet) ...
#define QgsDebugMsgLevel(str, level)
Definition: qgslogger.h:39
void setExtraColumns(int extraColumns)
Empty extra columns to announce from this model.
bool isValid() const
Checks if this expression is valid.
int columnCount(const QModelIndex &parent=QModelIndex()) const override
Returns the number of columns.
static QgsEditorWidgetRegistry * editorWidgetRegistry()
Returns the global editor widget registry, used for managing all known edit widget factories...
Definition: qgsgui.cpp:59
void initAttributes(int fieldCount)
Initialize this feature with the given number of fields.
Definition: qgsfeature.cpp:202
void afterRollBack()
Is emitted, after changes are rolled back.
bool isValid() const
isValid Check if this rule is valid.
static QList< QgsConditionalStyle > matchingConditionalStyles(const QList< QgsConditionalStyle > &styles, const QVariant &value, QgsExpressionContext &context)
Find and return the matching styles for the value and feature.
Defines left outer join from our vector layer to some other vector layer.
Every attribute editor widget needs a factory, which inherits this class.
This class wraps a request for features to a vector layer (or directly its vector data provider)...
An expression node which takes it value from a feature&#39;s field.
QColor backgroundColor() const
The background color for style.
bool removeRows(int row, int count, const QModelIndex &parent=QModelIndex()) override
Remove rows.
static QgsConditionalStyle compressStyles(const QList< QgsConditionalStyle > &styles)
Compress a list of styles into a single style.
const QgsFeatureRequest & request() const
Gets 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:48
void setId(QgsFeatureId id)
Sets the feature ID for this feature.
Definition: qgsfeature.cpp:112
void cachedLayerDeleted()
Is emitted when the cached layer is deleted.
QgsEditFormConfig editFormConfig
QgsFieldFormatter * fieldFormatter(const QString &id) const
Gets a field formatter by its id.
QgsVectorLayerJoinBuffer * joinBuffer()
Returns the join buffer object.
A field formatter helps to handle and display values for a field.
QModelIndexList idToIndexList(QgsFeatureId id) const
QString expression() const
Returns the original, unmodified expression string.
This class caches features of a given QgsVectorLayer.
static int debugLevel()
Reads the environment variable QGIS_DEBUG and converts it to int.
Definition: qgslogger.h:105
void progress(int i, bool &cancel)
#define FID_IS_NEW(fid)
Definition: qgsfeatureid.h:28
void modelChanged()
Model has been changed.
QColor textColor() const
The text color set for style.
QgsAttributeTableModel(QgsVectorLayerCache *layerCache, QObject *parent=nullptr)
Constructor.
int fieldCol(int idx) const
Gets column from field index.
void editCommandStarted(const QString &text)
Signal emitted when a new edit command has been started.
No filter is applied.
int fieldIdx(int col) const
Gets field index from column.
#define FID_TO_STRING(fid)
Definition: qgsfeatureid.h:30
virtual QString representValue(QgsVectorLayer *layer, int fieldIndex, const QVariantMap &config, const QVariant &cache, const QVariant &value) const
Create a pretty String representation of the value.
QgsEditorWidgetSetup findBest(const QgsVectorLayer *vl, const QString &fieldName) const
Find the best editor widget and its configuration for a given field.
int rowCount(const QModelIndex &parent=QModelIndex()) const override
Returns the number of rows.
Holder for the widget type and its configuration for a field.
bool featureAtId(QgsFeatureId featureId, QgsFeature &feature, bool skipCache=false)
Gets the feature at the given feature id.
bool validTextColor() const
Check if the text color is valid for render.
virtual bool isModified() const
Returns true if the provider has been modified since the last commit.
void executeMapLayerAction(QgsMapLayerAction *action, const QModelIndex &idx) const
Execute a QgsMapLayerAction.
const QgsVectorLayerJoinInfo * joinForFieldIndex(int index, const QgsFields &fields, int &sourceFieldIndex) const
Finds the vector join for a layer field index.
QgsFieldFormatter * fallbackFieldFormatter() const
Returns a basic fallback field formatter which can be used to represent any field in an unspectacular...
QgsFeatureId rowToId(int row) const
Maps row to feature id.
void dataChanged()
Data of layer changed.
int cacheSize()
Returns the maximum number of features this cache will hold.
void appendScopes(const QList< QgsExpressionContextScope *> &scopes)
Appends a list of scopes to the end of the context.
bool readOnly(int idx) const
This returns true if the field is manually set to read only or if the field does not support editing ...
QgsVectorDataProvider * dataProvider() FINAL
Returns the layer&#39;s data provider, it may be null.
QList< int > QgsAttributeList
Definition: qgsfield.h:27
bool isEditable() const
Returns whether joined fields may be edited through the form of the target layer. ...
QFont font() const
The font for the style.
void swapRows(QgsFeatureId a, QgsFeatureId b)
Swaps two rows.
bool nextFeature(QgsFeature &f)
void attributeDeleted(int idx)
Will be emitted, when an attribute has been deleted from this vector layer.
Geometry is not required. It may still be returned if e.g. required for a filter condition.
Represents a vector layer which manages a vector based data sets.
QVariant attribute(const QString &name) const
Lookup attribute value from attribute name.
Definition: qgsfeature.cpp:262
QgsFeatureIterator getFeatures(const QgsFeatureRequest &featureRequest=QgsFeatureRequest())
Query this VectorLayerCache for features.
void updatedFields()
Is emitted, whenever the fields available from this layer have been changed.
int idToRow(QgsFeatureId id) const
Maps feature id to table row.
bool setData(const QModelIndex &index, const QVariant &value, int role=Qt::EditRole) override
Updates data on given index.
void triggerForFeature(QgsMapLayer *layer, const QgsFeature *feature)
Triggers the action with the specified layer and feature.
void executeAction(QUuid action, const QModelIndex &idx) const
Execute an action.
Allows modification of attribute values.
An action which can run on map layers.
void prefetchColumnData(int column)
Caches the entire data for one column.
Qt::ItemFlags flags(const QModelIndex &index) const override
Returns item flags for the index.
QgsFeatureRequest & setFlags(QgsFeatureRequest::Flags flags)
Sets flags that affect how features will be fetched.