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