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