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