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