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