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