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