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