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