QGIS API Documentation  2.99.0-Master (37c43df)
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 "qgslogger.h"
28 #include "qgsmapcanvas.h"
30 #include "qgsmaplayerregistry.h"
31 #include "qgsrenderer.h"
32 #include "qgsvectorlayer.h"
33 #include "qgsvectordataprovider.h"
34 #include "qgssymbollayerutils.h"
35 
36 #include <QVariant>
37 
38 #include <limits>
39 
41  : QAbstractTableModel( parent )
42  , mLayerCache( layerCache )
43  , mFieldCount( 0 )
44  , mSortCacheExpression( QLatin1String( "" ) )
45  , mSortFieldIndex( -1 )
46  , mExtraColumns( 0 )
47 {
50  << QgsExpressionContextUtils::layerScope( layerCache->layer() );
51 
52  if ( layerCache->layer()->geometryType() == QgsWkbTypes::NullGeometry )
53  {
54  mFeatureRequest.setFlags( QgsFeatureRequest::NoGeometry );
55  }
56 
58 
59  if ( !layer()->hasGeometryType() )
60  mFeatureRequest.setFlags( QgsFeatureRequest::NoGeometry );
61 
63 
64  connect( mLayerCache, SIGNAL( attributeValueChanged( QgsFeatureId, int, const QVariant& ) ), this, SLOT( attributeValueChanged( QgsFeatureId, int, const QVariant& ) ) );
65  connect( layer(), SIGNAL( featuresDeleted( QgsFeatureIds ) ), this, SLOT( featuresDeleted( QgsFeatureIds ) ) );
66  connect( layer(), SIGNAL( attributeDeleted( int ) ), this, SLOT( attributeDeleted( int ) ) );
67  connect( layer(), SIGNAL( updatedFields() ), this, SLOT( updatedFields() ) );
68  connect( layer(), SIGNAL( editCommandEnded() ), this, SLOT( editCommandEnded() ) );
69  connect( mLayerCache, SIGNAL( featureAdded( QgsFeatureId ) ), this, SLOT( featureAdded( QgsFeatureId ) ) );
70  connect( mLayerCache, SIGNAL( cachedLayerDeleted() ), this, SLOT( 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;
94 }
95 
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  qSort( rows );
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  if ( row < 0 || count < 1 )
155  return false;
156 
157  beginRemoveRows( parent, row, row + count - 1 );
158 #ifdef QGISDEBUG
159  if ( 3 <= QgsLogger::debugLevel() )
160  QgsDebugMsgLevel( QString( "remove %2 rows at %1 (rows %3, ids %4)" ).arg( row ).arg( count ).arg( mRowIdMap.size() ).arg( mIdRowMap.size() ), 3 );
161 #endif
162 
163  // clean old references
164  for ( int i = row; i < row + count; i++ )
165  {
166  mSortCache.remove( mRowIdMap[i] );
167  mIdRowMap.remove( mRowIdMap[i] );
168  mRowIdMap.remove( i );
169  }
170 
171  // update maps
172  int n = mRowIdMap.size() + count;
173  for ( int i = row + count; i < n; i++ )
174  {
175  QgsFeatureId id = mRowIdMap[i];
176  mIdRowMap[id] -= count;
177  mRowIdMap[i-count] = id;
178  mRowIdMap.remove( i );
179  }
180 
181 #ifdef QGISDEBUG
182  if ( 4 <= QgsLogger::debugLevel() )
183  {
184  QgsDebugMsgLevel( QString( "after removal rows %1, ids %2" ).arg( mRowIdMap.size() ).arg( mIdRowMap.size() ), 4 );
185  QgsDebugMsgLevel( "id->row", 4 );
186  for ( QHash<QgsFeatureId, int>::const_iterator it = mIdRowMap.constBegin(); it != mIdRowMap.constEnd(); ++it )
187  QgsDebugMsgLevel( QString( "%1->%2" ).arg( FID_TO_STRING( it.key() ) ).arg( *it ), 4 );
188 
189  QgsDebugMsgLevel( "row->id", 4 );
190  for ( QHash<int, QgsFeatureId>::const_iterator it = mRowIdMap.constBegin(); it != mRowIdMap.constEnd(); ++it )
191  QgsDebugMsgLevel( QString( "%1->%2" ).arg( it.key() ).arg( FID_TO_STRING( *it ) ), 4 );
192  }
193 #endif
194 
195  Q_ASSERT( mRowIdMap.size() == mIdRowMap.size() );
196 
197  endRemoveRows();
198 
199  return true;
200 }
201 
203 {
204  QgsDebugMsgLevel( QString( "(%2) fid: %1" ).arg( fid ).arg( mFeatureRequest.filterType() ), 4 );
205  bool featOk = true;
206 
207  if ( mFeat.id() != fid )
208  featOk = loadFeatureAtId( fid );
209 
210  if ( featOk && mFeatureRequest.acceptFeature( mFeat ) )
211  {
212  if ( mSortFieldIndex == -1 )
213  {
215  mSortCache[mFeat.id()] = mSortCacheExpression.evaluate( &mExpressionContext );
216  }
217  else
218  {
219  QgsEditorWidgetFactory* widgetFactory = mWidgetFactories.at( mSortFieldIndex );
220  const QVariant& widgetCache = mAttributeWidgetCaches.at( mSortFieldIndex );
221  const QgsEditorWidgetConfig& widgetConfig = mWidgetConfigs.at( mSortFieldIndex );
222  QVariant sortValue = widgetFactory->representValue( layer(), mSortFieldIndex, widgetConfig, widgetCache, mFeat.attribute( mSortFieldIndex ) );
223  mSortCache.insert( mFeat.id(), sortValue );
224  }
225 
226  int n = mRowIdMap.size();
227  beginInsertRows( QModelIndex(), n, n );
228 
229  mIdRowMap.insert( fid, n );
230  mRowIdMap.insert( n, fid );
231 
232  endInsertRows();
233 
234  reload( index( rowCount() - 1, 0 ), index( rowCount() - 1, columnCount() ) );
235  }
236 }
237 
238 void QgsAttributeTableModel::updatedFields()
239 {
240  loadAttributes();
241  emit modelChanged();
242 }
243 
244 void QgsAttributeTableModel::editCommandEnded()
245 {
246  reload( createIndex( mChangedCellBounds.top(), mChangedCellBounds.left() ),
247  createIndex( mChangedCellBounds.bottom(), mChangedCellBounds.right() ) );
248 
249  mChangedCellBounds = QRect();
250 }
251 
252 void QgsAttributeTableModel::attributeDeleted( int idx )
253 {
254  if ( mSortCacheAttributes.contains( idx ) )
255  prefetchSortData( QLatin1String( "" ) );
256 }
257 
259 {
260  removeRows( 0, rowCount() );
261 
262  mAttributeWidgetCaches.clear();
263  mAttributes.clear();
264  mWidgetFactories.clear();
265  mWidgetConfigs.clear();
266 }
267 
268 void QgsAttributeTableModel::attributeValueChanged( QgsFeatureId fid, int idx, const QVariant &value )
269 {
270  QgsDebugMsgLevel( QString( "(%4) fid: %1, idx: %2, value: %3" ).arg( fid ).arg( idx ).arg( value.toString() ).arg( mFeatureRequest.filterType() ), 3 );
271 
272  if ( mSortCacheAttributes.contains( idx ) )
273  {
274  if ( mSortFieldIndex == -1 )
275  {
276  loadFeatureAtId( fid );
278  mSortCache[fid] = mSortCacheExpression.evaluate( &mExpressionContext );
279  }
280  else
281  {
282  QgsEditorWidgetFactory* widgetFactory = mWidgetFactories.at( mSortFieldIndex );
283  const QVariant& widgetCache = mAttributeWidgetCaches.at( mSortFieldIndex );
284  const QgsEditorWidgetConfig& widgetConfig = mWidgetConfigs.at( mSortFieldIndex );
285  QVariant sortValue = widgetFactory->representValue( layer(), mSortFieldIndex, widgetConfig, widgetCache, value );
286  mSortCache.insert( fid, sortValue );
287  }
288  }
289  // No filter request: skip all possibly heavy checks
290  if ( mFeatureRequest.filterType() == QgsFeatureRequest::FilterNone )
291  {
292  setData( index( idToRow( fid ), fieldCol( idx ) ), value, Qt::EditRole );
293  }
294  else
295  {
296  if ( loadFeatureAtId( fid ) )
297  {
298  if ( mFeatureRequest.acceptFeature( mFeat ) )
299  {
300  if ( !mIdRowMap.contains( fid ) )
301  {
302  // Feature changed in such a way, it will be shown now
303  featureAdded( fid );
304  }
305  else
306  {
307  // Update representation
308  setData( index( idToRow( fid ), fieldCol( idx ) ), value, Qt::EditRole );
309  }
310  }
311  else
312  {
313  if ( mIdRowMap.contains( fid ) )
314  {
315  // Feature changed such, that it is no longer shown
316  featuresDeleted( QgsFeatureIds() << fid );
317  }
318  // else: we don't care
319  }
320  }
321  }
322 }
323 
325 {
326  if ( !layer() )
327  {
328  return;
329  }
330 
331  bool ins = false, rm = false;
332 
333  QgsAttributeList attributes;
334  const QgsFields& fields = layer()->fields();
335 
336  mWidgetFactories.clear();
337  mAttributeWidgetCaches.clear();
338  mWidgetConfigs.clear();
339 
340  for ( int idx = 0; idx < fields.count(); ++idx )
341  {
342  const QgsEditorWidgetSetup setup = QgsEditorWidgetRegistry::instance()->findBest( layer(), fields[idx].name() );
344  if ( widgetFactory )
345  {
346  mWidgetFactories.append( widgetFactory );
347  mWidgetConfigs.append( setup.config() );
348  mAttributeWidgetCaches.append( widgetFactory->createCache( layer(), idx, setup.config() ) );
349 
350  attributes << idx;
351  }
352  }
353 
354  if ( mFieldCount + mExtraColumns < attributes.size() + mExtraColumns )
355  {
356  ins = true;
357  beginInsertColumns( QModelIndex(), mFieldCount + mExtraColumns, attributes.size() - 1 );
358  }
359  else if ( attributes.size() + mExtraColumns < mFieldCount + mExtraColumns )
360  {
361  rm = true;
362  beginRemoveColumns( QModelIndex(), attributes.size(), mFieldCount + mExtraColumns - 1 );
363  }
364 
365  mFieldCount = attributes.size();
366  mAttributes = attributes;
367 
368  if ( mSortFieldIndex >= mAttributes.count() )
369  mSortFieldIndex = -1;
370 
371  if ( ins )
372  {
373  endInsertColumns();
374  }
375  else if ( rm )
376  {
377  endRemoveColumns();
378  }
379 }
380 
382 {
383  // make sure attributes are properly updated before caching the data
384  // (emit of progress() signal may enter event loop and thus attribute
385  // table view may be updated with inconsistent model which may assume
386  // wrong number of attributes)
387  loadAttributes();
388 
389  beginResetModel();
390 
391  if ( rowCount() != 0 )
392  {
393  removeRows( 0, rowCount() );
394  }
395 
396  QgsFeatureIterator features = mLayerCache->getFeatures( mFeatureRequest );
397 
398  int i = 0;
399 
400  QTime t;
401  t.start();
402 
403  while ( features.nextFeature( mFeat ) )
404  {
405  ++i;
406 
407  if ( t.elapsed() > 1000 )
408  {
409  bool cancel = false;
410  emit progress( i, cancel );
411  if ( cancel )
412  break;
413 
414  t.restart();
415  }
416  featureAdded( mFeat.id() );
417  }
418 
419  emit finished();
420 
421  connect( mLayerCache, SIGNAL( invalidated() ), this, SLOT( loadLayer() ), Qt::UniqueConnection );
422 
423  endResetModel();
424 }
425 
427 {
428  if ( fieldName.isNull() )
429  {
430  mRowStylesMap.clear();
431  emit dataChanged( index( 0, 0 ), index( rowCount() - 1, columnCount() - 1 ) );
432  return;
433  }
434 
435  int fieldIndex = mLayerCache->layer()->fields().lookupField( fieldName );
436  if ( fieldIndex == -1 )
437  return;
438 
439  //whole column has changed
440  int col = fieldCol( fieldIndex );
441  emit dataChanged( index( 0, col ), index( rowCount() - 1, col ) );
442 }
443 
445 {
446  if ( a == b )
447  return;
448 
449  int rowA = idToRow( a );
450  int rowB = idToRow( b );
451 
452  //emit layoutAboutToBeChanged();
453 
454  mRowIdMap.remove( rowA );
455  mRowIdMap.remove( rowB );
456  mRowIdMap.insert( rowA, b );
457  mRowIdMap.insert( rowB, a );
458 
459  mIdRowMap.remove( a );
460  mIdRowMap.remove( b );
461  mIdRowMap.insert( a, rowB );
462  mIdRowMap.insert( b, rowA );
463 
464  //emit layoutChanged();
465 }
466 
468 {
469  if ( !mIdRowMap.contains( id ) )
470  {
471  QgsDebugMsg( QString( "idToRow: id %1 not in the map" ).arg( id ) );
472  return -1;
473  }
474 
475  return mIdRowMap[id];
476 }
477 
479 {
480  return index( idToRow( id ), 0 );
481 }
482 
484 {
485  QModelIndexList indexes;
486 
487  int row = idToRow( id );
488  int columns = columnCount();
489  indexes.reserve( columns );
490  for ( int column = 0; column < columns; ++column )
491  {
492  indexes.append( index( row, column ) );
493  }
494 
495  return indexes;
496 }
497 
499 {
500  if ( !mRowIdMap.contains( row ) )
501  {
502  QgsDebugMsg( QString( "rowToId: row %1 not in the map" ).arg( row ) );
503  // return negative infinite (to avoid collision with newly added features)
505  }
506 
507  return mRowIdMap[row];
508 }
509 
511 {
512  return mAttributes[col];
513 }
514 
516 {
517  return mAttributes.indexOf( idx );
518 }
519 
520 int QgsAttributeTableModel::rowCount( const QModelIndex &parent ) const
521 {
522  Q_UNUSED( parent );
523  return mRowIdMap.size();
524 }
525 
526 int QgsAttributeTableModel::columnCount( const QModelIndex &parent ) const
527 {
528  Q_UNUSED( parent );
529  return qMax( 1, mFieldCount + mExtraColumns ); // if there are zero columns all model indices will be considered invalid
530 }
531 
532 QVariant QgsAttributeTableModel::headerData( int section, Qt::Orientation orientation, int role ) const
533 {
534  if ( !layer() )
535  return QVariant();
536 
537  if ( role == Qt::DisplayRole )
538  {
539  if ( orientation == Qt::Vertical ) //row
540  {
541  return QVariant( section );
542  }
543  else if ( section >= 0 && section < mFieldCount )
544  {
545  QString attributeName = layer()->fields().at( mAttributes.at( section ) ).displayName();
546  return QVariant( attributeName );
547  }
548  else
549  {
550  return tr( "extra column" );
551  }
552  }
553  else if ( role == Qt::ToolTipRole )
554  {
555  if ( orientation == Qt::Vertical )
556  {
557  // TODO show DisplayExpression
558  return tr( "Feature ID: %1" ).arg( rowToId( section ) );
559  }
560  else
561  {
562  QgsField field = layer()->fields().at( mAttributes.at( section ) );
563  return field.name();
564  }
565  }
566  else
567  {
568  return QVariant();
569  }
570 }
571 
572 QVariant QgsAttributeTableModel::data( const QModelIndex &index, int role ) const
573 {
574  if ( !index.isValid() ||
575  ( role != Qt::TextAlignmentRole
576  && role != Qt::DisplayRole
577  && role != Qt::EditRole
578  && role != SortRole
579  && role != FeatureIdRole
580  && role != FieldIndexRole
581  && role != Qt::BackgroundColorRole
582  && role != Qt::TextColorRole
583  && role != Qt::DecorationRole
584  && role != Qt::FontRole
585  )
586  )
587  return QVariant();
588 
589  QgsFeatureId rowId = rowToId( index.row() );
590 
591  if ( role == FeatureIdRole )
592  return rowId;
593 
594  if ( index.column() >= mFieldCount )
595  return QVariant();
596 
597  int fieldId = mAttributes.at( index.column() );
598 
599  if ( role == FieldIndexRole )
600  return fieldId;
601 
602  if ( role == SortRole )
603  {
604  return mSortCache[rowId];
605  }
606 
607  QgsField field = layer()->fields().at( fieldId );
608 
609  if ( role == Qt::TextAlignmentRole )
610  {
611  return mWidgetFactories.at( index.column() )->alignmentFlag( layer(), fieldId, mWidgetConfigs.at( index.column() ) );
612  }
613 
614  if ( mFeat.id() != rowId || !mFeat.isValid() )
615  {
616  if ( !loadFeatureAtId( rowId ) )
617  return QVariant( "ERROR" );
618 
619  if ( mFeat.id() != rowId )
620  return QVariant( "ERROR" );
621  }
622 
623  QVariant val = mFeat.attribute( fieldId );
624 
625  switch ( role )
626  {
627  case Qt::DisplayRole:
628  return mWidgetFactories.at( index.column() )->representValue( layer(), fieldId, mWidgetConfigs.at( index.column() ),
629  mAttributeWidgetCaches.at( index.column() ), val );
630 
631  case Qt::EditRole:
632  return val;
633 
634  case Qt::BackgroundColorRole:
635  case Qt::TextColorRole:
636  case Qt::DecorationRole:
637  case Qt::FontRole:
638  {
640  QList<QgsConditionalStyle> styles;
641  if ( mRowStylesMap.contains( index.row() ) )
642  {
643  styles = mRowStylesMap[index.row()];
644  }
645  else
646  {
647  styles = QgsConditionalStyle::matchingConditionalStyles( layer()->conditionalStyles()->rowStyles(), QVariant(), mExpressionContext );
648  mRowStylesMap.insert( index.row(), styles );
649 
650  }
651 
653  styles = layer()->conditionalStyles()->fieldStyles( field.name() );
655  styles.insert( 0, rowstyle );
657 
658  if ( style.isValid() )
659  {
660  if ( role == Qt::BackgroundColorRole && style.validBackgroundColor() )
661  return style.backgroundColor();
662  if ( role == Qt::TextColorRole && style.validTextColor() )
663  return style.textColor();
664  if ( role == Qt::DecorationRole )
665  return style.icon();
666  if ( role == Qt::FontRole )
667  return style.font();
668  }
669 
670  return QVariant();
671  }
672  }
673 
674  return QVariant();
675 }
676 
677 bool QgsAttributeTableModel::setData( const QModelIndex &index, const QVariant &value, int role )
678 {
679  Q_UNUSED( value )
680 
681  if ( !index.isValid() || index.column() >= mFieldCount || role != Qt::EditRole || !layer()->isEditable() )
682  return false;
683 
684  if ( !layer()->isModified() )
685  return false;
686 
687  if ( mChangedCellBounds.isNull() )
688  {
689  mChangedCellBounds = QRect( index.column(), index.row(), 1, 1 );
690  }
691  else
692  {
693  if ( index.column() < mChangedCellBounds.left() )
694  {
695  mChangedCellBounds.setLeft( index.column() );
696  }
697  if ( index.row() < mChangedCellBounds.top() )
698  {
699  mChangedCellBounds.setTop( index.row() );
700  }
701  if ( index.column() > mChangedCellBounds.right() )
702  {
703  mChangedCellBounds.setRight( index.column() );
704  }
705  if ( index.row() > mChangedCellBounds.bottom() )
706  {
707  mChangedCellBounds.setBottom( index.row() );
708  }
709  }
710 
711  return true;
712 }
713 
714 Qt::ItemFlags QgsAttributeTableModel::flags( const QModelIndex &index ) const
715 {
716  if ( !index.isValid() )
717  return Qt::ItemIsEnabled;
718 
719  if ( index.column() >= mFieldCount )
720  return Qt::NoItemFlags;
721 
722  Qt::ItemFlags flags = QAbstractItemModel::flags( index );
723 
724  if ( layer()->isEditable() &&
725  !layer()->editFormConfig().readOnly( mAttributes[index.column()] ) &&
726  (( layer()->dataProvider() && layer()->dataProvider()->capabilities() & QgsVectorDataProvider::ChangeAttributeValues ) ||
727  FID_IS_NEW( rowToId( index.row() ) ) ) )
728  flags |= Qt::ItemIsEditable;
729 
730  return flags;
731 }
732 
733 void QgsAttributeTableModel::reload( const QModelIndex &index1, const QModelIndex &index2 )
734 {
736  emit dataChanged( index1, index2 );
737 }
738 
739 
740 void QgsAttributeTableModel::executeAction( const QUuid& action, const QModelIndex &idx ) const
741 {
742  QgsFeature f = feature( idx );
743  layer()->actions()->doAction( action, f, fieldIdx( idx.column() ) );
744 }
745 
746 void QgsAttributeTableModel::executeMapLayerAction( QgsMapLayerAction* action, const QModelIndex &idx ) const
747 {
748  QgsFeature f = feature( idx );
749  action->triggerForFeature( layer(), &f );
750 }
751 
752 QgsFeature QgsAttributeTableModel::feature( const QModelIndex &idx ) const
753 {
754  QgsFeature f;
755  f.initAttributes( mAttributes.size() );
756  f.setFeatureId( rowToId( idx.row() ) );
757  for ( int i = 0; i < mAttributes.size(); i++ )
758  {
759  f.setAttribute( mAttributes[i], data( index( idx.row(), i ), Qt::EditRole ) );
760  }
761 
762  return f;
763 }
764 
766 {
767  if ( column == -1 || column >= mAttributes.count() )
768  {
769  prefetchSortData( QLatin1String( "" ) );
770  }
771  else
772  {
774  }
775 }
776 
777 void QgsAttributeTableModel::prefetchSortData( const QString& expressionString )
778 {
779  mSortCache.clear();
780  mSortCacheAttributes.clear();
781  mSortFieldIndex = -1;
782  mSortCacheExpression = QgsExpression( expressionString );
783 
784  QgsEditorWidgetFactory* widgetFactory = nullptr;
785  QVariant widgetCache;
786  QgsEditorWidgetConfig widgetConfig;
787 
788  if ( mSortCacheExpression.isField() )
789  {
790  QString fieldName = static_cast<const QgsExpression::NodeColumnRef*>( mSortCacheExpression.rootNode() )->name();
791  mSortFieldIndex = mLayerCache->layer()->fields().lookupField( fieldName );
792  }
793 
794  if ( mSortFieldIndex == -1 )
795  {
796  mSortCacheExpression.prepare( &mExpressionContext );
797 
798  Q_FOREACH ( const QString& col, mSortCacheExpression.referencedColumns() )
799  {
800  mSortCacheAttributes.append( mLayerCache->layer()->fields().lookupField( col ) );
801  }
802  }
803  else
804  {
805  mSortCacheAttributes.append( mSortFieldIndex );
806 
807  widgetFactory = mWidgetFactories.at( mSortFieldIndex );
808  widgetCache = mAttributeWidgetCaches.at( mSortFieldIndex );
809  widgetConfig = mWidgetConfigs.at( mSortFieldIndex );
810  }
811 
812  QgsFeatureRequest request = QgsFeatureRequest( mFeatureRequest )
814  .setSubsetOfAttributes( mSortCacheAttributes );
815  QgsFeatureIterator it = mLayerCache->getFeatures( request );
816 
817  QgsFeature f;
818  while ( it.nextFeature( f ) )
819  {
820  if ( mSortFieldIndex == -1 )
821  {
823  mSortCache.insert( f.id(), mSortCacheExpression.evaluate( &mExpressionContext ) );
824  }
825  else
826  {
827  QVariant sortValue = widgetFactory->sortValue( layer(), mSortFieldIndex, widgetConfig, widgetCache, f.attribute( mSortFieldIndex ) );
828  mSortCache.insert( f.id(), sortValue );
829  }
830  }
831 }
832 
834 {
835  if ( mSortCacheExpression.rootNode() )
836  return mSortCacheExpression.expression();
837  else
838  return QString();
839 }
840 
842 {
843  mFeatureRequest = request;
844  if ( layer() && !layer()->hasGeometryType() )
845  mFeatureRequest.setFlags( mFeatureRequest.flags() | QgsFeatureRequest::NoGeometry );
846 }
847 
849 {
850  return mFeatureRequest;
851 }
int lookupField(const QString &fieldName) const
Look up field&#39;s index from the field name.
Definition: qgsfields.cpp:291
bool isValid() const
Returns the validity of this feature.
Definition: qgsfeature.cpp:200
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()
Get all layer actions defined on this layer.
QgsFeatureId id
Definition: qgsfeature.h:139
Wrapper for iterator of features from vector data provider or vector layer.
QgsVectorLayer * layer() const
Returns the layer this model uses as backend.
int extraColumns() const
Empty extra columns to announce from this model.
static unsigned index
bool acceptFeature(const QgsFeature &feature)
Check if a feature is accepted by this requests filter.
virtual void featuresDeleted(const QgsFeatureIds &fids)
Launched when eatures have been deleted.
virtual void loadLayer()
Loads the layer into the model Preferably to be called, before using this model as source for any oth...
static QString quotedColumnRef(QString name)
Returns a quoted column reference (in double quotes)
const Flags & flags() const
QString name
Definition: qgsfield.h:55
QHash< int, QgsFeatureId > mRowIdMap
Get the field index of this column.
QgsEditorWidgetFactory * factory(const QString &widgetId)
Get a factory for the given widget type id.
void setFeature(const QgsFeature &feature)
Convenience function for setting a feature for the context.
#define QgsDebugMsg(str)
Definition: qgslogger.h:33
QSet< QgsFeatureId > QgsFeatureIds
Definition: qgsfeature.h:355
virtual void layerDeleted()
Launched when layer has been deleted.
QList< QgsConditionalStyle > fieldStyles(const QString &fieldName)
Returns the conditional styles set for the field UI properties.
QgsEditorWidgetConfig config() const
QVariant evaluate()
Evaluate the feature and return the result.
void reload(const QModelIndex &index1, const QModelIndex &index2)
Reloads the model data between indices.
#define FID_TO_STRING(fid)
Definition: qgsfeature.h:40
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.
QgsWkbTypes::GeometryType geometryType() const
Returns point, line or polygon.
Container of fields for a vector layer.
Definition: qgsfields.h:36
bool setAttribute(int field, const QVariant &attr)
Set an attribute&#39;s value by field index.
Definition: qgsfeature.cpp:228
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.
QSet< QString > referencedColumns() const
Get list of columns referenced by the expression.
The feature class encapsulates a single feature including its id, geometry and a list of field/values...
Definition: qgsfeature.h:135
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 values.
virtual bool isEditable() const override
Returns true if the provider is in editing mode.
static QgsEditorWidgetRegistry * instance()
This class is a singleton and has therefore to be accessed with this method instead of a constructor...
int count() const
Return number of items.
Definition: qgsfields.cpp:117
void resetModel()
Resets the model.
virtual QVariant createCache(QgsVectorLayer *vl, int fieldIdx, const QgsEditorWidgetConfig &config)
Create a cache for a given field.
QgsVectorLayerCache * mLayerCache
QModelIndex idToIndex(QgsFeatureId id) const
QgsFields fields() const
Returns the list of fields of this layer.
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:137
virtual void attributeValueChanged(QgsFeatureId fid, int idx, const QVariant &value)
Launched when attribute value has been changed.
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.
const Node * rootNode() const
Returns root node of the expression. Root node is null is parsing has failed.
QgsFeature feature(const QModelIndex &idx) const
Return the feature attributes at given model index.
QVariantMap QgsEditorWidgetConfig
Holds a set of configuration parameters for a editor widget wrapper.
QVariant headerData(int section, Qt::Orientation orientation, int role=Qt::DisplayRole) const override
Returns header data.
static QgsExpressionContextScope * globalScope()
Creates a new scope which contains variables and functions relating to the global QGIS context...
virtual QVariant sortValue(QgsVectorLayer *vl, int fieldIdx, const QgsEditorWidgetConfig &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...
Conditional styling for a rule.
void setFeatureId(QgsFeatureId id)
Sets the feature ID for this feature.
Definition: qgsfeature.cpp:124
#define QgsDebugMsgLevel(str, level)
Definition: qgslogger.h:34
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.
QVector< QgsEditorWidgetFactory * > mWidgetFactories
QgsExpressionContext mExpressionContext
void initAttributes(int fieldCount)
Initialize this feature with the given number of fields.
Definition: qgsfeature.cpp:219
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)...
QList< int > QgsAttributeList
virtual QString representValue(QgsVectorLayer *vl, int fieldIdx, const QgsEditorWidgetConfig &config, const QVariant &cache, const QVariant &value) const
Create a pretty String representation of the value.
QColor backgroundColor() const
The background color for style.
QVector< QgsEditorWidgetConfig > mWidgetConfigs
bool removeRows(int row, int count, const QModelIndex &parent=QModelIndex()) override
Remove rows.
QHash< QgsFeatureId, int > mIdRowMap
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.
Encapsulate a field in an attribute table or data source.
Definition: qgsfield.h:47
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
virtual void featureAdded(QgsFeatureId fid)
Launched when a feature has been added.
QModelIndexList idToIndexList(QgsFeatureId id) const
QString expression() const
Return 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:93
void progress(int i, bool &cancel)
void modelChanged()
Model has been changed.
QVector< QVariant > mAttributeWidgetCaches
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
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 prepare(const QgsExpressionContext *context)
Get the expression ready for evaluation - find out column indexes.
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.
bool isField() const
Checks whether an expression consists only of a single field reference.
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.
virtual void loadAttributes()
Gets mFieldCount, mAttributes and mValueMaps.
QgsFeatureId rowToId(int row) const
Maps row to feature id.
qint64 QgsFeatureId
Definition: qgsfeature.h:32
QString sortCacheExpression() const
The expression which was used to fill the sorting cache.
#define FID_IS_NEW(fid)
Definition: qgsfeature.h:38
static QgsExpressionContextScope * projectScope()
Creates a new scope which contains variables and functions relating to the current QGIS project...
QFont font() const
The font for the style.
static QgsExpressionContextScope * layerScope(const QgsMapLayer *layer)
Creates a new scope which contains variables and functions relating to a QgsMapLayer.
void swapRows(QgsFeatureId a, QgsFeatureId b)
Swaps two rows.
bool nextFeature(QgsFeature &f)
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:277
QgsFeatureIterator getFeatures(const QgsFeatureRequest &featureRequest=QgsFeatureRequest())
Query this VectorLayerCache for features.
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.
QHash< int, QList< QgsConditionalStyle > > mRowStylesMap
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.