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