QGIS API Documentation 4.3.0-Master (16eddb2670e)
Loading...
Searching...
No Matches
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
17
18#include <limits>
19
20#include "qgsactionmanager.h"
21#include "qgsapplication.h"
22#include "qgsconditionalstyle.h"
24#include "qgsexpression.h"
27#include "qgsfeatureiterator.h"
28#include "qgsfieldformatter.h"
30#include "qgsfieldmodel.h"
31#include "qgsfields.h"
32#include "qgsgui.h"
33#include "qgslogger.h"
34#include "qgsmaplayeraction.h"
35#include "qgsstringutils.h"
37#include "qgsvectorlayer.h"
38#include "qgsvectorlayercache.h"
39#include "qgsvectorlayerutils.h"
40
41#include <QString>
42#include <QUuid>
43#include <QVariant>
44
45#include "moc_qgsattributetablemodel.cpp"
46
47using namespace Qt::StringLiterals;
48
50 : QAbstractTableModel( parent )
51 , mLayer( layerCache->layer() )
52 , mLayerCache( layerCache )
53{
54 mExpressionContext.appendScopes( QgsExpressionContextUtils::globalProjectLayerScopes( layerCache->layer() ) );
55
56 if ( mLayer->geometryType() == Qgis::GeometryType::Null )
57 {
58 mFeatureRequest.setFlags( Qgis::FeatureRequestFlag::NoGeometry );
59 }
60
61 mFeat.setId( std::numeric_limits<int>::min() );
62
63 if ( !mLayer->isSpatial() )
64 mFeatureRequest.setFlags( Qgis::FeatureRequestFlag::NoGeometry );
65
66 loadAttributes();
67
68 connect( mLayer, &QgsVectorLayer::featuresDeleted, this, &QgsAttributeTableModel::featuresDeleted );
69 connect( mLayer, &QgsVectorLayer::attributeDeleted, this, &QgsAttributeTableModel::attributeDeleted );
70 connect( mLayer, &QgsVectorLayer::updatedFields, this, &QgsAttributeTableModel::updatedFields );
71
72 connect( mLayer, &QgsVectorLayer::editCommandStarted, this, &QgsAttributeTableModel::bulkEditCommandStarted );
73 connect( mLayer, &QgsVectorLayer::beforeRollBack, this, &QgsAttributeTableModel::bulkEditCommandStarted );
74 connect( mLayer, &QgsVectorLayer::afterRollBack, this, [this] {
75 mIsCleaningUpAfterRollback = true;
76 bulkEditCommandEnded();
77 mIsCleaningUpAfterRollback = false;
78 } );
79
80 connect( mLayer, &QgsVectorLayer::editCommandEnded, this, &QgsAttributeTableModel::editCommandEnded );
81 connect( mLayerCache, &QgsVectorLayerCache::attributeValueChanged, this, &QgsAttributeTableModel::attributeValueChanged );
82 connect( mLayerCache, &QgsVectorLayerCache::featureAdded, this, [this]( QgsFeatureId id ) { featureAdded( id ); } );
83 connect( mLayerCache, &QgsVectorLayerCache::cachedLayerDeleted, this, &QgsAttributeTableModel::layerDeleted );
84}
85
86bool QgsAttributeTableModel::loadFeatureAtId( QgsFeatureId fid ) const
87{
88 QgsDebugMsgLevel( u"loading feature %1"_s.arg( fid ), 3 );
89
90 if ( fid == std::numeric_limits<int>::min() )
91 {
92 return false;
93 }
94
95 return mLayerCache->featureAtId( fid, mFeat );
96}
97
98bool QgsAttributeTableModel::loadFeatureAtId( QgsFeatureId fid, int fieldIdx ) const
99{
100 QgsDebugMsgLevel( u"loading feature %1 with field %2"_s.arg( fid ).arg( fieldIdx ), 3 );
101
102 if ( mLayerCache->cacheSubsetOfAttributes().contains( fieldIdx ) )
103 {
104 return loadFeatureAtId( fid );
105 }
106
107 if ( fid == std::numeric_limits<int>::min() )
108 {
109 return false;
110 }
111 return mLayerCache->featureAtIdWithAllAttributes( fid, mFeat );
112}
113
115{
116 return mExtraColumns;
117}
118
120{
121 if ( extraColumns > mExtraColumns )
122 {
123 beginInsertColumns( QModelIndex(), mFieldCount + mExtraColumns, mFieldCount + extraColumns - 1 );
124 mExtraColumns = extraColumns;
125 endInsertColumns();
126 }
127 else if ( extraColumns < mExtraColumns )
128 {
129 beginRemoveColumns( QModelIndex(), mFieldCount + extraColumns, mFieldCount + mExtraColumns - 1 );
130 mExtraColumns = extraColumns;
131 endRemoveColumns();
132 }
133}
134
135void QgsAttributeTableModel::featuresDeleted( const QgsFeatureIds &fids )
136{
137 QList<int> rows;
138
139 const auto constFids = fids;
140 for ( const QgsFeatureId fid : constFids )
141 {
142 QgsDebugMsgLevel( u"(%2) fid: %1, size: %3"_s.arg( fid ).arg( qgsEnumValueToKey( mFeatureRequest.filterType() ) ).arg( mIdRowMap.size() ), 4 );
143
144 const int row = idToRow( fid );
145 if ( row != -1 )
146 rows << row;
147 }
148
149 std::sort( rows.begin(), rows.end() );
150
151 int lastRow = -1;
152 int beginRow = -1;
153 int currentRowCount = 0;
154 int removedRows = 0;
155 bool reset = false;
156
157 const auto constRows = rows;
158 for ( const int row : constRows )
159 {
160#if 0
161 qDebug() << "Row: " << row << ", begin " << beginRow << ", last " << lastRow << ", current " << currentRowCount << ", removed " << removedRows;
162#endif
163 if ( lastRow == -1 )
164 {
165 beginRow = row;
166 }
167
168 if ( row != lastRow + 1 && lastRow != -1 )
169 {
170 if ( rows.count() > 100 && currentRowCount < 10 )
171 {
172 reset = true;
173 break;
174 }
175 removeRows( beginRow - removedRows, currentRowCount );
176
177 beginRow = row;
178 removedRows += currentRowCount;
179 currentRowCount = 0;
180 }
181
182 currentRowCount++;
183
184 lastRow = row;
185 }
186
187 if ( !reset )
188 removeRows( beginRow - removedRows, currentRowCount );
189 else
190 resetModel();
191}
192
193bool QgsAttributeTableModel::removeRows( int row, int count, const QModelIndex &parent )
194{
195 if ( row < 0 || count < 1 )
196 return false;
197
198 if ( !mResettingModel )
199 {
200 beginRemoveRows( parent, row, row + count - 1 );
201 }
202
203 QgsDebugMsgLevel( u"remove %2 rows at %1 (rows %3, ids %4)"_s.arg( row ).arg( count ).arg( mRowIdMap.size() ).arg( mIdRowMap.size() ), 3 );
204
205 if ( mBulkEditCommandRunning && !mResettingModel )
206 {
207 for ( int i = row; i < row + count; i++ )
208 {
209 const QgsFeatureId fid { rowToId( row ) };
210 mInsertedRowsChanges.removeOne( fid );
211 }
212 }
213
214 // clean old references
215 for ( int i = row; i < row + count; i++ )
216 {
217 for ( SortCache &cache : mSortCaches )
218 cache.sortCache.remove( mRowIdMap[i] );
219 mIdRowMap.remove( mRowIdMap[i] );
220 mRowIdMap.remove( i );
221 }
222
223 // update maps
224 const int n = mRowIdMap.size() + count;
225 for ( int i = row + count; i < n; i++ )
226 {
227 const QgsFeatureId id = mRowIdMap[i];
228 mIdRowMap[id] -= count;
229 mRowIdMap[i - count] = id;
230 mRowIdMap.remove( i );
231 }
232
233#ifdef QGISDEBUG
234 if ( 4 <= QgsLogger::debugLevel() )
235 {
236 QgsDebugMsgLevel( u"after removal rows %1, ids %2"_s.arg( mRowIdMap.size() ).arg( mIdRowMap.size() ), 4 );
237 QgsDebugMsgLevel( u"id->row"_s, 4 );
238 for ( QHash<QgsFeatureId, int>::const_iterator it = mIdRowMap.constBegin(); it != mIdRowMap.constEnd(); ++it )
239 {
240 QgsDebugMsgLevel( u"%1->%2"_s.arg( FID_TO_STRING( it.key() ) ).arg( *it ), 4 );
241 }
242
243 QgsDebugMsgLevel( u"row->id"_s, 4 );
244 for ( QHash<int, QgsFeatureId>::const_iterator it = mRowIdMap.constBegin(); it != mRowIdMap.constEnd(); ++it )
245 {
246 QgsDebugMsgLevel( u"%1->%2"_s.arg( it.key() ).arg( FID_TO_STRING( *it ) ), 4 );
247 }
248 }
249#endif
250
251 Q_ASSERT( mRowIdMap.size() == mIdRowMap.size() );
252
253 if ( !mResettingModel )
254 endRemoveRows();
255
256 return true;
257}
258
259void QgsAttributeTableModel::featureAdded( QgsFeatureId fid )
260{
261 QgsDebugMsgLevel( u"(%2) fid: %1"_s.arg( fid ).arg( qgsEnumValueToKey( mFeatureRequest.filterType() ) ), 4 );
262 bool featOk = true;
263
264 if ( mFeat.id() != fid )
265 featOk = loadFeatureAtId( fid );
266
267 if ( featOk && mFeatureRequest.acceptFeature( mFeat ) )
268 {
269 for ( SortCache &cache : mSortCaches )
270 {
271 if ( cache.sortFieldIndex >= 0 )
272 {
273 const WidgetData &widgetData = getWidgetData( cache.sortFieldIndex );
274 const QVariant sortValue = widgetData.fieldFormatter->sortValue( mLayer, cache.sortFieldIndex, widgetData.config, widgetData.cache, mFeat.attribute( cache.sortFieldIndex ) );
275 cache.sortCache.insert( mFeat.id(), sortValue );
276 }
277 else if ( cache.sortCacheExpression.isValid() )
278 {
279 mExpressionContext.setFeature( mFeat );
280 cache.sortCache[mFeat.id()] = cache.sortCacheExpression.evaluate( &mExpressionContext );
281 }
282 }
283
284 // Skip if the fid is already in the map (do not add twice)!
285 if ( !mIdRowMap.contains( fid ) )
286 {
287 const int n = mRowIdMap.size();
288 if ( !mResettingModel )
289 beginInsertRows( QModelIndex(), n, n );
290 mIdRowMap.insert( fid, n );
291 mRowIdMap.insert( n, fid );
292 if ( !mResettingModel )
293 endInsertRows();
294
295 const int row = rowCount() - 1;
296 const QModelIndex first = index( row, 0 );
297 const int lastColumn = columnCount() - 1;
298 const QModelIndex last = index( row, lastColumn );
299 reload( first, last );
300 if ( mBulkEditCommandRunning && !mResettingModel )
301 {
302 mInsertedRowsChanges.append( fid );
303 }
304 }
305 }
306}
307
308void QgsAttributeTableModel::updatedFields()
309{
310 loadAttributes();
311 emit modelChanged();
312}
313
314void QgsAttributeTableModel::editCommandEnded()
315{
316 // do not do reload(...) due would trigger (dataChanged) row sort
317 // giving issue: https://github.com/qgis/QGIS/issues/23892
318 bulkEditCommandEnded();
319}
320
321void QgsAttributeTableModel::attributeDeleted( int idx )
322{
323 int cacheIndex = 0;
324 for ( const SortCache &cache : mSortCaches )
325 {
326 if ( cache.sortCacheAttributes.contains( idx ) )
327 {
328 prefetchSortData( QString(), cacheIndex );
329 }
330 cacheIndex++;
331 }
332}
333
334void QgsAttributeTableModel::layerDeleted()
335{
336 mLayerCache = nullptr;
337 mLayer = nullptr;
338 removeRows( 0, rowCount() );
339
340 mAttributes.clear();
341 mWidgetDatas.clear();
342}
343
344void QgsAttributeTableModel::fieldFormatterRemoved( QgsFieldFormatter *fieldFormatter )
345{
346 for ( WidgetData &widgetData : mWidgetDatas )
347 {
348 if ( widgetData.fieldFormatter == fieldFormatter )
349 widgetData.fieldFormatter = QgsApplication::fieldFormatterRegistry()->fallbackFieldFormatter();
350 }
351}
352
353void QgsAttributeTableModel::attributeValueChanged( QgsFeatureId fid, int idx, const QVariant &value )
354{
355 // Defer all updates if a bulk edit/rollback command is running
356 if ( mBulkEditCommandRunning )
357 {
358 mAttributeValueChanges.insert( QPair<QgsFeatureId, int>( fid, idx ), value );
359 return;
360 }
361 QgsDebugMsgLevel( u"(%4) fid: %1, idx: %2, value: %3"_s.arg( fid ).arg( idx ).arg( value.toString() ).arg( qgsEnumValueToKey( mFeatureRequest.filterType() ) ), 2 );
362
363 for ( SortCache &cache : mSortCaches )
364 {
365 if ( cache.sortCacheAttributes.contains( idx ) )
366 {
367 if ( cache.sortFieldIndex == -1 )
368 {
369 if ( loadFeatureAtId( fid ) )
370 {
371 mExpressionContext.setFeature( mFeat );
372 cache.sortCache[fid] = cache.sortCacheExpression.evaluate( &mExpressionContext );
373 }
374 }
375 else
376 {
377 const WidgetData &widgetData = getWidgetData( cache.sortFieldIndex );
378 const QVariant sortValue = widgetData.fieldFormatter->representValue( mLayer, cache.sortFieldIndex, widgetData.config, widgetData.cache, value );
379 cache.sortCache.insert( fid, sortValue );
380 }
381 }
382 }
383 // No filter request: skip all possibly heavy checks
384 if ( mFeatureRequest.filterType() == Qgis::FeatureRequestFilterType::NoFilter )
385 {
386 if ( loadFeatureAtId( fid ) )
387 {
388 const QModelIndex modelIndex = index( idToRow( fid ), fieldCol( idx ) );
389 setData( modelIndex, value, Qt::EditRole );
390 emit dataChanged( modelIndex, modelIndex, QVector<int>() << Qt::DisplayRole );
391 }
392 }
393 else
394 {
395 if ( loadFeatureAtId( fid ) )
396 {
397 if ( mFeatureRequest.acceptFeature( mFeat ) )
398 {
399 if ( !mIdRowMap.contains( fid ) )
400 {
401 // Feature changed in such a way, it will be shown now
402 featureAdded( fid );
403 }
404 else
405 {
406 // Update representation
407 const QModelIndex modelIndex = index( idToRow( fid ), fieldCol( idx ) );
408 setData( modelIndex, value, Qt::EditRole );
409 emit dataChanged( modelIndex, modelIndex, QVector<int>() << Qt::DisplayRole );
410 }
411 }
412 else
413 {
414 if ( mIdRowMap.contains( fid ) )
415 {
416 // Feature changed such, that it is no longer shown
417 featuresDeleted( QgsFeatureIds() << fid );
418 }
419 // else: we don't care
420 }
421 }
422 }
423}
424
425void QgsAttributeTableModel::loadAttributes()
426{
427 if ( !mLayer )
428 {
429 return;
430 }
431
432 const QgsFields fields = mLayer->fields();
433 if ( mFields == fields )
434 return;
435
436 bool ins = false, rm = false;
437
438 QgsAttributeList attributes;
439
440 for ( int idx = 0; idx < fields.count(); ++idx )
441 {
442 attributes << idx;
443 }
444
445 if ( mFieldCount + mExtraColumns < attributes.size() + mExtraColumns )
446 {
447 ins = true;
448 beginInsertColumns( QModelIndex(), mFieldCount + mExtraColumns, attributes.size() - 1 );
449 }
450 else if ( attributes.size() + mExtraColumns < mFieldCount + mExtraColumns )
451 {
452 rm = true;
453 beginRemoveColumns( QModelIndex(), attributes.size(), mFieldCount + mExtraColumns - 1 );
454 }
455
456 mFields = fields;
457
458 mFieldCount = attributes.size();
459 mAttributes = attributes;
460 mWidgetDatas.clear();
461 mWidgetDatas.resize( mFieldCount );
462
463 for ( SortCache &cache : mSortCaches )
464 {
465 if ( cache.sortFieldIndex >= mAttributes.count() )
466 cache.sortFieldIndex = -1;
467 }
468
469 if ( ins )
470 {
471 endInsertColumns();
472 }
473 else if ( rm )
474 {
475 endRemoveColumns();
476 }
477}
478
480{
481 // make sure attributes are properly updated before caching the data
482 // (emit of progress() signal may enter event loop and thus attribute
483 // table view may be updated with inconsistent model which may assume
484 // wrong number of attributes)
485
486 loadAttributes();
487
488 mResettingModel = true;
489 beginResetModel();
490
491 if ( rowCount() != 0 )
492 {
493 removeRows( 0, rowCount() );
494 }
495
496 // Layer might have been deleted and cache set to nullptr!
497 if ( mLayerCache )
498 {
499 QgsFeatureIterator features = mLayerCache->getFeatures( mFeatureRequest );
500
501 int i = 0;
502
503 QElapsedTimer t;
504 t.start();
505
506 while ( features.nextFeature( mFeat ) )
507 {
508 ++i;
509
510 if ( t.elapsed() > 1000 )
511 {
512 bool cancel = false;
513 emit progress( i, cancel );
514 if ( cancel )
515 break;
516
517 t.restart();
518 }
519 featureAdded( mFeat.id() );
520 }
521
522 emit finished();
523 connect( mLayerCache, &QgsVectorLayerCache::invalidated, this, &QgsAttributeTableModel::loadLayer, Qt::UniqueConnection );
524 }
525
526 endResetModel();
527
528 mResettingModel = false;
529}
530
531
533{
534 if ( fieldName.isNull() )
535 {
536 mRowStylesMap.clear();
537 mConstraintStylesMap.clear();
538 emit dataChanged( index( 0, 0 ), index( rowCount() - 1, columnCount() - 1 ) );
539 return;
540 }
541
542 const int fieldIndex = mLayer->fields().lookupField( fieldName );
543 if ( fieldIndex == -1 )
544 return;
545
546 //whole column has changed
547 const int col = fieldCol( fieldIndex );
548 emit dataChanged( index( 0, col ), index( rowCount() - 1, col ) );
549}
550
552{
553 if ( a == b )
554 return;
555
556 const int rowA = idToRow( a );
557 const int rowB = idToRow( b );
558
559 //emit layoutAboutToBeChanged();
560
561 mRowIdMap.remove( rowA );
562 mRowIdMap.remove( rowB );
563 mRowIdMap.insert( rowA, b );
564 mRowIdMap.insert( rowB, a );
565
566 mIdRowMap.remove( a );
567 mIdRowMap.remove( b );
568 mIdRowMap.insert( a, rowB );
569 mIdRowMap.insert( b, rowA );
570 Q_ASSERT( mRowIdMap.size() == mIdRowMap.size() );
571
572
573 //emit layoutChanged();
574}
575
577{
578 if ( !mIdRowMap.contains( id ) )
579 {
580 QgsDebugError( u"idToRow: id %1 not in the map"_s.arg( id ) );
581 return -1;
582 }
583
584 return mIdRowMap[id];
585}
586
588{
589 return index( idToRow( id ), 0 );
590}
591
593{
594 QModelIndexList indexes;
595
596 const int row = idToRow( id );
597 const int columns = columnCount();
598 indexes.reserve( columns );
599 for ( int column = 0; column < columns; ++column )
600 {
601 indexes.append( index( row, column ) );
602 }
603
604 return indexes;
605}
606
608{
609 if ( !mRowIdMap.contains( row ) )
610 {
611 QgsDebugError( u"rowToId: row %1 not in the map"_s.arg( row ) );
612 // return negative infinite (to avoid collision with newly added features)
613 return std::numeric_limits<int>::min();
614 }
615
616 return mRowIdMap[row];
617}
618
620{
621 return mAttributes[col];
622}
623
625{
626 return mAttributes.indexOf( idx );
627}
628
629int QgsAttributeTableModel::rowCount( const QModelIndex &parent ) const
630{
631 if ( parent.isValid() )
632 return 0;
633
634 return mRowIdMap.size();
635}
636
637int QgsAttributeTableModel::columnCount( const QModelIndex &parent ) const
638{
639 if ( parent.isValid() )
640 return 0;
641
642 return std::max( 1, mFieldCount + mExtraColumns ); // if there are zero columns all model indices will be considered invalid
643}
644
645QVariant QgsAttributeTableModel::headerData( int section, Qt::Orientation orientation, int role ) const
646{
647 if ( !mLayer )
648 return QVariant();
649
650 if ( role == Qt::DisplayRole )
651 {
652 if ( orientation == Qt::Vertical ) //row
653 {
654 return QVariant( section );
655 }
656 else if ( section >= 0 && section < mFieldCount )
657 {
658 const QString attributeName = mLayer->fields().at( mAttributes.at( section ) ).displayName();
659 return QVariant( attributeName );
660 }
661 else
662 {
663 return tr( "extra column" );
664 }
665 }
666 else if ( role == Qt::ToolTipRole )
667 {
668 if ( orientation == Qt::Vertical )
669 {
670 // TODO show DisplayExpression
671 return tr( "Feature ID: %1" ).arg( rowToId( section ) );
672 }
673 else
674 {
675 const QgsField field = mLayer->fields().at( mAttributes.at( section ) );
676 return QgsFieldModel::fieldToolTipExtended( field, mLayer );
677 }
678 }
679 else
680 {
681 return QVariant();
682 }
683}
684
685QVariant QgsAttributeTableModel::data( const QModelIndex &index, int role ) const
686{
687 if ( !index.isValid()
688 || !mLayer
689 || ( role != Qt::TextAlignmentRole && role != Qt::DisplayRole && role != Qt::ToolTipRole && role != Qt::EditRole && role != static_cast<int>( CustomRole::FeatureId ) && role != static_cast<int>( CustomRole::FieldIndex ) && role != Qt::BackgroundRole && role != Qt::ForegroundRole && role != Qt::DecorationRole && role != Qt::FontRole && role < static_cast<int>( CustomRole::Sort ) ) )
690 return QVariant();
691
692 const QgsFeatureId rowId = rowToId( index.row() );
693
694 if ( role == static_cast<int>( CustomRole::FeatureId ) )
695 return rowId;
696
697 if ( index.column() >= mFieldCount )
698 return QVariant();
699
700 const int fieldId = mAttributes.at( index.column() );
701
702 if ( role == static_cast<int>( CustomRole::FieldIndex ) )
703 return fieldId;
704
705 if ( role >= static_cast<int>( CustomRole::Sort ) )
706 {
707 const unsigned long cacheIndex = role - static_cast<int>( CustomRole::Sort );
708 if ( cacheIndex < mSortCaches.size() )
709 {
710 return mSortCaches.at( cacheIndex ).sortCache.value( rowId );
711 }
712 else
713 {
714 return QVariant();
715 }
716 }
717
718 const QgsField field = mFields.at( fieldId );
719
720 if ( role == Qt::TextAlignmentRole )
721 {
722 const WidgetData &widgetData = getWidgetData( index.column() );
723 return static_cast<Qt::Alignment::Int>( widgetData.fieldFormatter->alignmentFlag( field, widgetData.config ) | Qt::AlignVCenter );
724 }
725
726 if ( mFeat.id() != rowId || !mFeat.isValid() || !mLayerCache->cacheSubsetOfAttributes().contains( fieldId ) )
727 {
728 if ( !loadFeatureAtId( rowId, fieldId ) )
729 return QVariant( "ERROR" );
730
731 if ( mFeat.id() != rowId )
732 return QVariant( "ERROR" );
733 }
734
735 QVariant val = mFeat.attribute( fieldId );
736
737 switch ( role )
738 {
739 case Qt::DisplayRole:
740 {
741 const WidgetData &widgetData = getWidgetData( index.column() );
742 QString s = widgetData.fieldFormatter->representValue( mLayer, fieldId, widgetData.config, widgetData.cache, val );
743 // In table view, too long strings kill performance. Just truncate them
744 constexpr int MAX_STRING_LENGTH = 10 * 1000;
745 if ( static_cast<size_t>( s.size() ) > static_cast<size_t>( MAX_STRING_LENGTH ) )
746 {
747 s.resize( MAX_STRING_LENGTH );
748 s.append( tr( "... truncated ..." ) );
749 }
750 return s;
751 }
752 case Qt::ToolTipRole:
753 {
754 const WidgetData &widgetData = getWidgetData( index.column() );
755 QString tooltip = widgetData.fieldFormatter->representValue( mLayer, fieldId, widgetData.config, widgetData.cache, val );
756 if ( tooltip != val.toString() )
757 {
758 tooltip = tr( "%1 (%2)" ).arg( tooltip, val.toString() );
759 }
760 if ( val.userType() == QMetaType::Type::QString && QgsStringUtils::isUrl( val.toString() ) )
761 {
762 tooltip = tr( "%1 (Ctrl+click to open)" ).arg( tooltip );
763 }
764 return tooltip;
765 }
766 case Qt::EditRole:
767 return val;
768
769 case Qt::BackgroundRole:
770 case Qt::ForegroundRole:
771 case Qt::DecorationRole:
772 case Qt::FontRole:
773 {
774 mExpressionContext.setFeature( mFeat );
775 QList<QgsConditionalStyle> styles;
776 if ( mRowStylesMap.contains( mFeat.id() ) )
777 {
778 styles = mRowStylesMap[mFeat.id()];
779 }
780 else
781 {
782 styles = QgsConditionalStyle::matchingConditionalStyles( mLayer->conditionalStyles()->rowStyles(), QVariant(), mExpressionContext );
783 mRowStylesMap.insert( mFeat.id(), styles );
784 }
786
787 QgsConditionalStyle constraintstyle;
788 if ( mShowValidityState && QgsVectorLayerUtils::attributeHasConstraints( mLayer, fieldId ) )
789 {
790 if ( mConstraintStylesMap.contains( mFeat.id() ) && mConstraintStylesMap[mFeat.id()].contains( fieldId ) )
791 {
792 constraintstyle = mConstraintStylesMap[mFeat.id()][fieldId];
793 }
794 else
795 {
796 QStringList errors;
798 {
799 constraintstyle = mLayer->conditionalStyles()->constraintFailureStyles( QgsFieldConstraints::ConstraintStrengthHard );
800 }
801 else
802 {
804 {
805 constraintstyle = mLayer->conditionalStyles()->constraintFailureStyles( QgsFieldConstraints::ConstraintStrengthSoft );
806 }
807 }
808 mConstraintStylesMap[mFeat.id()].insert( fieldId, constraintstyle );
809 }
810 }
811
812 styles = mLayer->conditionalStyles()->fieldStyles( field.name() );
813 styles = QgsConditionalStyle::matchingConditionalStyles( styles, val, mExpressionContext );
814 styles.insert( 0, rowstyle );
815 styles.insert( 0, constraintstyle );
817
818 if ( style.isValid() )
819 {
820 if ( role == Qt::BackgroundRole && style.validBackgroundColor() )
821 return style.backgroundColor();
822 if ( role == Qt::ForegroundRole && style.validTextColor() )
823 return style.textColor();
824 if ( role == Qt::DecorationRole )
825 return style.icon();
826 if ( role == Qt::FontRole )
827 return style.font();
828 }
829 else if ( val.userType() == QMetaType::Type::QString && QgsStringUtils::isUrl( val.toString() ) )
830 {
831 if ( role == Qt::ForegroundRole )
832 {
833 return QColor( Qt::blue );
834 }
835 else if ( role == Qt::FontRole )
836 {
837 QFont font;
838 font.setUnderline( true );
839 return font;
840 }
841 }
842
843 return QVariant();
844 }
845 }
846
847 return QVariant();
848}
849
850bool QgsAttributeTableModel::setData( const QModelIndex &index, const QVariant &value, int role )
851{
852 Q_UNUSED( value )
853
854 if ( !index.isValid() || index.column() >= mFieldCount || role != Qt::EditRole || !mLayer->isEditable() )
855 return false;
856
857 mRowStylesMap.remove( mFeat.id() );
858 mConstraintStylesMap.remove( mFeat.id() );
859
860 if ( !mLayer->isModified() )
861 return false;
862
863 return true;
864}
865
866Qt::ItemFlags QgsAttributeTableModel::flags( const QModelIndex &index ) const
867{
868 if ( !index.isValid() )
869 return Qt::NoItemFlags;
870
871 if ( index.column() >= mFieldCount || !mLayer )
872 return Qt::NoItemFlags;
873
874 Qt::ItemFlags flags = QAbstractTableModel::flags( index );
875
876 const int fieldIndex = mAttributes[index.column()];
877 const QgsFeatureId fid = rowToId( index.row() );
878
879 if ( QgsVectorLayerUtils::fieldIsEditable( mLayer, fieldIndex, fid ) )
880 flags |= Qt::ItemIsEditable;
881
882 return flags;
883}
884
885void QgsAttributeTableModel::bulkEditCommandStarted()
886{
887 mBulkEditCommandRunning = true;
888 mAttributeValueChanges.clear();
889}
890
891void QgsAttributeTableModel::bulkEditCommandEnded()
892{
893 mBulkEditCommandRunning = false;
894 // Full model update if the changed rows are more than half the total rows
895 // or if their count is > layer cache size
896
897 const long long fullModelUpdateThreshold = std::min<long long>( mLayerCache->cacheSize(), std::ceil( rowCount() * 0.5 ) );
898 bool fullModelUpdate = false;
899
900 // try the cheaper check first
901 if ( mInsertedRowsChanges.size() > fullModelUpdateThreshold )
902 {
903 fullModelUpdate = true;
904 }
905 else
906 {
907 QSet<QgsFeatureId> changedRows;
908 changedRows.reserve( mAttributeValueChanges.size() );
909 // we need to count changed features, not the total of changed attributes (which may all apply to one feature)
910 for ( auto it = mAttributeValueChanges.constBegin(); it != mAttributeValueChanges.constEnd(); ++it )
911 {
912 changedRows.insert( it.key().first );
913 if ( changedRows.size() > fullModelUpdateThreshold )
914 {
915 fullModelUpdate = true;
916 break;
917 }
918 }
919 }
920
921 QgsDebugMsgLevel( u"Bulk edit command ended modified rows over (%3), cache size is %1, starting %2 update."_s.arg( mLayerCache->cacheSize() ).arg( fullModelUpdate ? u"full"_s : u"incremental"_s ).arg( rowCount() ), 3 );
922
923 // Remove added rows on rollback
924 if ( mIsCleaningUpAfterRollback )
925 {
926 for ( const int fid : std::as_const( mInsertedRowsChanges ) )
927 {
928 const int row( idToRow( fid ) );
929 if ( row < 0 )
930 {
931 continue;
932 }
933 removeRow( row );
934 }
935 }
936
937 // Invalidates the whole model
938 if ( fullModelUpdate )
939 {
940 // Invalidates the cache (there is no API for doing this directly)
941 emit mLayer->dataChanged();
942 if ( rowCount() > 0 )
943 {
944 emit dataChanged( createIndex( 0, 0 ), createIndex( rowCount() - 1, columnCount() - 1 ) );
945 }
946 }
947 else if ( !mAttributeValueChanges.empty() && !mInsertedRowsChanges.empty() )
948 {
949 int minRow = std::numeric_limits< int >::max();
950 int minCol = std::numeric_limits< int >::max();
951 int maxRow = -1;
952 int maxCol = -1;
953 const int lastColumn = columnCount() - 1;
954 for ( auto it = mAttributeValueChanges.constBegin(); it != mAttributeValueChanges.constEnd(); ++it )
955 {
956 attributeValueChanged( it.key().first, it.key().second, it.value() );
957 const int row( idToRow( it.key().first ) );
958 const int col( fieldCol( it.key().second ) );
959 minRow = std::min<int>( row, minRow );
960 minCol = std::min<int>( col, minCol );
961 maxRow = std::max<int>( row, maxRow );
962 maxCol = std::max<int>( col, maxCol );
963 }
964
965 for ( QgsFeatureId fid : std::as_const( mInsertedRowsChanges ) )
966 {
967 const int row( idToRow( fid ) );
968 minRow = std::min<int>( row, minRow );
969 maxRow = std::max<int>( row, maxRow );
970 // the whole row has changed, so we need to expand the range to include ALL columns
971 minCol = 0;
972 maxCol = lastColumn;
973 }
974#ifdef QGISDEBUG
975 Q_ASSERT_X( minRow >= 0 && minRow < rowCount(), "QgsAttributeTableModel::bulkEditCommandEnded", u"Invalid minRow, got %1 expected >=0 && < %2"_s.arg( minRow ).arg( rowCount() ).toLocal8Bit().constData() );
976 Q_ASSERT_X( minCol >= 0 && minCol < columnCount(), "QgsAttributeTableModel::bulkEditCommandEnded", u"Invalid minCol, got %1 expected >=0 && < %2"_s.arg( minCol ).arg( columnCount() ).toLocal8Bit().constData() );
977 Q_ASSERT_X( maxRow >= 0 && maxRow < rowCount(), "QgsAttributeTableModel::bulkEditCommandEnded", u"Invalid maxRow, got %1 expected >=0 && < %2"_s.arg( maxRow ).arg( rowCount() ).toLocal8Bit().constData() );
978 Q_ASSERT_X( maxCol >= 0 && maxCol < columnCount(), "QgsAttributeTableModel::bulkEditCommandEnded", u"Invalid maxCol, got %1 expected >=0 && < %2"_s.arg( maxCol ).arg( columnCount() ).toLocal8Bit().constData() );
979#endif
980
981 emit dataChanged( createIndex( minRow, minCol ), createIndex( maxRow, maxCol ) );
982 }
983 mAttributeValueChanges.clear();
984}
985
986void QgsAttributeTableModel::reload( const QModelIndex &index1, const QModelIndex &index2 )
987{
988 mFeat.setId( std::numeric_limits<int>::min() );
989
990#ifdef QGISDEBUG
991 Q_ASSERT_X( index1.row() >= 0 && index1.row() < rowCount(), "QgsAttributeTableModel::reload", u"Invalid min row, got %1 expected >=0 && < %2"_s.arg( index1.row() ).arg( rowCount() ).toLocal8Bit().constData() );
992 Q_ASSERT_X(
993 index1.column() >= 0 && index1.column() < columnCount(),
994 "QgsAttributeTableModel::reload",
995 u"Invalid min col, got %1 expected >=0 && < %2"_s.arg( index1.column() ).arg( columnCount() ).toLocal8Bit().constData()
996 );
997 Q_ASSERT_X( index2.row() >= 0 && index2.row() < rowCount(), "QgsAttributeTableModel::reload", u"Invalid max row, got %1 expected >=0 && < %2"_s.arg( index2.row() ).arg( rowCount() ).toLocal8Bit().constData() );
998 Q_ASSERT_X(
999 index2.column() >= 0 && index2.column() < columnCount(),
1000 "QgsAttributeTableModel::reload",
1001 u"Invalid max col, got %1 expected >=0 && < %2"_s.arg( index2.column() ).arg( columnCount() ).toLocal8Bit().constData()
1002 );
1003 Q_ASSERT_X( index2.row() >= index1.row(), "QgsAttributeTableModel::reload", u"Invalid max row, got %1 expected >= %2"_s.arg( index2.row() ).arg( index1.row() ).toLocal8Bit().constData() );
1004 Q_ASSERT_X( index2.column() >= index1.column(), "QgsAttributeTableModel::reload", u"Invalid max column, got %1 expected >= %2"_s.arg( index2.column() ).arg( index1.column() ).toLocal8Bit().constData() );
1005#endif
1006
1007 if ( !mResettingModel )
1008 {
1009 emit dataChanged( index1, index2 );
1010 }
1011}
1012
1013void QgsAttributeTableModel::executeAction( QUuid action, const QModelIndex &idx ) const
1014{
1015 const QgsFeature f = feature( idx );
1016 mLayer->actions()->doAction( action, f, fieldIdx( idx.column() ) );
1017}
1018
1019void QgsAttributeTableModel::executeMapLayerAction( QgsMapLayerAction *action, const QModelIndex &idx, const QgsMapLayerActionContext &context ) const
1020{
1021 const QgsFeature f = feature( idx );
1023 action->triggerForFeature( mLayer, f );
1025 action->triggerForFeature( mLayer, f, context );
1026}
1027
1028QgsFeature QgsAttributeTableModel::feature( const QModelIndex &idx ) const
1029{
1030 QgsFeature f( mLayer->fields() );
1031 f.initAttributes( mAttributes.size() );
1032 f.setId( rowToId( idx.row() ) );
1033 for ( int i = 0; i < mAttributes.size(); i++ )
1034 {
1035 f.setAttribute( mAttributes[i], data( index( idx.row(), i ), Qt::EditRole ) );
1036 }
1037
1038 return f;
1039}
1040
1042{
1043 if ( column == -1 || column >= mAttributes.count() )
1044 {
1045 prefetchSortData( QString() );
1046 }
1047 else
1048 {
1049 prefetchSortData( QgsExpression::quotedColumnRef( mLayer->fields().at( mAttributes.at( column ) ).name() ) );
1050 }
1051}
1052
1053void QgsAttributeTableModel::prefetchSortData( const QString &expressionString, unsigned long cacheIndex )
1054{
1055 if ( !mLayer )
1056 return;
1057
1058 if ( cacheIndex >= mSortCaches.size() )
1059 {
1060 mSortCaches.resize( cacheIndex + 1 );
1061 }
1062 SortCache &cache = mSortCaches[cacheIndex];
1063 cache.sortCache.clear();
1064 cache.sortCacheAttributes.clear();
1065 cache.sortFieldIndex = -1;
1066 if ( !expressionString.isEmpty() )
1067 cache.sortCacheExpression = QgsExpression( expressionString );
1068 else
1069 {
1070 // no sorting
1071 cache.sortCacheExpression = QgsExpression();
1072 return;
1073 }
1074
1075 WidgetData widgetData;
1076
1077 if ( cache.sortCacheExpression.isField() )
1078 {
1079 const QString fieldName = static_cast<const QgsExpressionNodeColumnRef *>( cache.sortCacheExpression.rootNode() )->name();
1080 cache.sortFieldIndex = mLayer->fields().lookupField( fieldName );
1081 }
1082
1083 if ( cache.sortFieldIndex == -1 )
1084 {
1085 cache.sortCacheExpression.prepare( &mExpressionContext );
1086
1087 const QSet<QString> &referencedColumns = cache.sortCacheExpression.referencedColumns();
1088
1089 for ( const QString &col : referencedColumns )
1090 {
1091 cache.sortCacheAttributes.append( mLayer->fields().lookupField( col ) );
1092 }
1093 }
1094 else
1095 {
1096 cache.sortCacheAttributes.append( cache.sortFieldIndex );
1097
1098 widgetData = getWidgetData( cache.sortFieldIndex );
1099 }
1100
1101 const QgsFeatureRequest request = QgsFeatureRequest( mFeatureRequest )
1103 .setSubsetOfAttributes( cache.sortCacheAttributes );
1104
1105 QgsFeatureIterator it = mLayerCache->getFeatures( request );
1106
1107 QgsFeature f;
1108 while ( it.nextFeature( f ) )
1109 {
1110 if ( cache.sortFieldIndex == -1 )
1111 {
1112 mExpressionContext.setFeature( f );
1113 const QVariant cacheValue = cache.sortCacheExpression.evaluate( &mExpressionContext );
1114 cache.sortCache.insert( f.id(), cacheValue );
1115 }
1116 else
1117 {
1118 const QVariant sortValue = widgetData.fieldFormatter->sortValue( mLayer, cache.sortFieldIndex, widgetData.config, widgetData.cache, f.attribute( cache.sortFieldIndex ) );
1119 cache.sortCache.insert( f.id(), sortValue );
1120 }
1121 }
1122}
1123
1124QString QgsAttributeTableModel::sortCacheExpression( unsigned long cacheIndex ) const
1125{
1126 QString expressionString;
1127
1128 if ( cacheIndex >= mSortCaches.size() )
1129 return expressionString;
1130
1131 const QgsExpression &expression = mSortCaches[cacheIndex].sortCacheExpression;
1132
1133 if ( expression.isValid() )
1134 expressionString = expression.expression();
1135 else
1136 expressionString = QString();
1137
1138 return expressionString;
1139}
1140
1142{
1143 if ( !mFeatureRequest.compare( request ) )
1144 {
1145 mFeatureRequest = request;
1146 if ( mLayer && !mLayer->isSpatial() )
1147 mFeatureRequest.setFlags( mFeatureRequest.flags() | Qgis::FeatureRequestFlag::NoGeometry );
1148 // Prefetch data for sorting, resetting all caches
1149 for ( unsigned long i = 0; i < mSortCaches.size(); ++i )
1150 {
1152 }
1153 }
1154}
1155
1157{
1158 return mFeatureRequest;
1159}
1160
1161const QgsAttributeTableModel::WidgetData &QgsAttributeTableModel::getWidgetData( int column ) const
1162{
1163 Q_ASSERT( column >= 0 && column < mAttributes.size() );
1164
1165 WidgetData &widgetData = mWidgetDatas[column];
1166 if ( !widgetData.loaded )
1167 {
1168 const int idx = fieldIdx( column );
1169 const QgsEditorWidgetSetup setup = QgsGui::editorWidgetRegistry()->findBest( mLayer, mFields[idx].name() );
1170 widgetData.fieldFormatter = QgsApplication::fieldFormatterRegistry()->fieldFormatter( setup.type() );
1171 widgetData.config = setup.config();
1172 widgetData.cache = widgetData.fieldFormatter->createCache( mLayer, idx, setup.config() );
1173 widgetData.loaded = true;
1174 }
1175
1176 return widgetData;
1177}
@ NoFilter
No filter is applied.
Definition qgis.h:2408
@ NoGeometry
Geometry is not required. It may still be returned if e.g. required for a filter condition.
Definition qgis.h:2380
@ NoFlags
No flags are set.
Definition qgis.h:2379
@ Null
No geometry.
Definition qgis.h:404
static QgsFieldFormatterRegistry * fieldFormatterRegistry()
Gets the registry of available field formatters.
const QgsFeatureRequest & request() const
Gets the the feature request.
bool removeRows(int row, int count, const QModelIndex &parent=QModelIndex()) override
Remove rows.
Qt::ItemFlags flags(const QModelIndex &index) const override
Returns item flags for the index.
QgsFeature feature(const QModelIndex &idx) const
Returns the feature attributes at given model index.
void resetModel()
Resets the model.
void fieldConditionalStyleChanged(const QString &fieldName)
Handles updating the model when the conditional style for a field changes.
QString sortCacheExpression(unsigned long cacheIndex=0) const
The expression which was used to fill the sorting cache at index cacheIndex.
int fieldIdx(int col) const
Gets field index from column.
QgsAttributeTableModel(QgsVectorLayerCache *layerCache, QObject *parent=nullptr)
Constructor.
void swapRows(QgsFeatureId a, QgsFeatureId b)
Swaps two rows.
void modelChanged()
Emitted when the model has been changed.
void executeMapLayerAction(QgsMapLayerAction *action, const QModelIndex &idx, const QgsMapLayerActionContext &context=QgsMapLayerActionContext()) const
Execute a QgsMapLayerAction.
void progress(int i, bool &cancel)
void setRequest(const QgsFeatureRequest &request)
Set a request that will be used to fill this attribute table model.
bool setData(const QModelIndex &index, const QVariant &value, int role=Qt::EditRole) override
Updates data on given index.
int rowCount(const QModelIndex &parent=QModelIndex()) const override
Returns the number of rows.
QModelIndex idToIndex(QgsFeatureId id) const
QgsVectorLayer * layer() const
Returns the layer this model uses as backend.
int extraColumns() const
Empty extra columns to announce from this model.
QgsVectorLayerCache * layerCache() const
Returns the layer cache this model uses as backend.
void finished()
Emitted when the model has completely loaded all features.
int columnCount(const QModelIndex &parent=QModelIndex()) const override
Returns the number of columns.
QVariant headerData(int section, Qt::Orientation orientation, int role=Qt::DisplayRole) const override
Returns header data.
QModelIndexList idToIndexList(QgsFeatureId id) const
void prefetchSortData(const QString &expression, unsigned long cacheIndex=0)
Prefetches the entire data for an expression.
QgsFeatureId rowToId(int row) const
Maps row to feature id.
int idToRow(QgsFeatureId id) const
Maps feature id to table row.
virtual void loadLayer()
Loads the layer into the model Preferably to be called, before using this model as source for any oth...
void setExtraColumns(int extraColumns)
Empty extra columns to announce from this model.
void prefetchColumnData(int column)
Caches the entire data for one column.
int fieldCol(int idx) const
Gets column from field index.
void reload(const QModelIndex &index1, const QModelIndex &index2)
Reloads the model data between indices.
@ FeatureId
Get the feature id of the feature in this row.
@ Sort
Role used for sorting start here.
@ FieldIndex
Get the field index of this column.
void executeAction(QUuid action, const QModelIndex &idx) const
Execute an action.
QVariant data(const QModelIndex &index, int role) const override
Returns data on the given index.
Conditional styling for a rule.
static QgsConditionalStyle compressStyles(const QList< QgsConditionalStyle > &styles)
Compress a list of styles into a single style.
static QList< QgsConditionalStyle > matchingConditionalStyles(const QList< QgsConditionalStyle > &styles, const QVariant &value, QgsExpressionContext &context)
Find and return the matching styles for the value and feature.
QColor backgroundColor() const
The background color for style.
QColor textColor() const
The text color set for style.
QFont font() const
The font for the style.
bool validTextColor() const
Check if the text color is valid for render.
bool isValid() const
isValid Check if this rule is valid.
QPixmap icon() const
The icon set for style generated from the set symbol.
bool validBackgroundColor() const
Check if the background color is valid for render.
QgsEditorWidgetSetup findBest(const QgsVectorLayer *vl, const QString &fieldName) const
Find the best editor widget and its configuration for a given field.
Holder for the widget type and its configuration for a field.
QString type() const
Returns the widget type to use.
QVariantMap config() const
Returns the widget configuration.
static QList< QgsExpressionContextScope * > globalProjectLayerScopes(const QgsMapLayer *layer)
Creates a list of three scopes: global, layer's project and layer.
An expression node which takes its value from a feature's field.
QString name() const
The name of the column.
Handles parsing and evaluation of expressions (formerly called "search strings").
bool prepare(const QgsExpressionContext *context)
Gets the expression ready for evaluation - find out column indexes.
QString expression() const
Returns the original, unmodified expression string.
bool isField() const
Checks whether an expression consists only of a single field reference.
QSet< QString > referencedColumns() const
Gets list of columns referenced by the expression.
static QString quotedColumnRef(QString name)
Returns a quoted column reference (in double quotes).
const QgsExpressionNode * rootNode() const
Returns the root node of the expression.
bool needsGeometry() const
Returns true if the expression uses feature geometry for some computation.
QVariant evaluate()
Evaluate the feature and return the result.
bool isValid() const
Checks if this expression is valid.
Wrapper for iterator of features from vector data provider or vector layer.
bool nextFeature(QgsFeature &f)
Fetch next feature and stores in f, returns true on success.
Wraps a request for features to a vector layer (or directly its vector data provider).
QgsFeatureRequest & setFlags(Qgis::FeatureRequestFlags flags)
Sets flags that affect how features will be fetched.
Qgis::FeatureRequestFilterType filterType() const
Returns the attribute/ID filter type which is currently set on this request.
QgsFeatureRequest & setSubsetOfAttributes(const QgsAttributeList &attrs)
Set a subset of attributes that will be fetched.
bool acceptFeature(const QgsFeature &feature)
Check if a feature is accepted by this requests filter.
The feature class encapsulates a single feature including its unique ID, geometry and a list of field...
Definition qgsfeature.h:60
Q_INVOKABLE bool setAttribute(int field, const QVariant &attr)
Sets an attribute's value by field index.
void initAttributes(int fieldCount)
Initialize this feature with the given number of fields.
QgsFeatureId id
Definition qgsfeature.h:63
void setId(QgsFeatureId id)
Sets the feature id for this feature.
Q_INVOKABLE QVariant attribute(const QString &name) const
Lookup attribute value by attribute name.
@ ConstraintStrengthSoft
User is warned if constraint is violated but feature can still be accepted.
@ ConstraintStrengthHard
Constraint must be honored before feature can be accepted.
QgsFieldFormatter * fallbackFieldFormatter() const
Returns a basic fallback field formatter which can be used to represent any field in an unspectacular...
QgsFieldFormatter * fieldFormatter(const QString &id) const
Gets a field formatter by its id.
A field formatter helps to handle and display values for a field.
virtual Q_DECL_DEPRECATED Qt::AlignmentFlag alignmentFlag(QgsVectorLayer *layer, int fieldIndex, const QVariantMap &config) const SIP_DEPRECATED
Returns the alignment for a particular field.
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.
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.
static QString fieldToolTipExtended(const QgsField &field, const QgsVectorLayer *layer, const QString &predefinedComment=QString())
Returns a HTML formatted tooltip string for a field, containing details like the field name,...
Encapsulate a field in an attribute table or data source.
Definition qgsfield.h:56
QString name
Definition qgsfield.h:65
int count
Definition qgsfields.h:49
static QgsEditorWidgetRegistry * editorWidgetRegistry()
Returns the global editor widget registry, used for managing all known edit widget factories.
Definition qgsgui.cpp:110
static int debugLevel()
Reads the environment variable QGIS_DEBUG and converts it to int.
Definition qgslogger.h:175
Encapsulates the context in which a QgsMapLayerAction action is executed.
An action which can run on map layers.
virtual Q_DECL_DEPRECATED void triggerForFeature(QgsMapLayer *layer, const QgsFeature &feature)
Triggers the action with the specified layer and feature.
static bool isUrl(const QString &string)
Returns whether the string is a URL (http,https,ftp,file).
Caches features for a given QgsVectorLayer.
void invalidated()
The cache has been invalidated and cleared.
void featureAdded(QgsFeatureId fid)
Emitted when a new feature has been added to the layer and this cache.
void cachedLayerDeleted()
Is emitted when the cached layer is deleted.
void attributeValueChanged(QgsFeatureId fid, int field, const QVariant &value)
Emitted when an attribute is changed.
static bool fieldIsEditable(const QgsVectorLayer *layer, int fieldIndex, const QgsFeature &feature, QgsVectorLayerUtils::FieldIsEditableFlags flags=QgsVectorLayerUtils::FieldIsEditableFlags())
Tests whether a field is editable for a particular feature.
static bool attributeHasConstraints(const QgsVectorLayer *layer, int attributeIndex)
Returns true if a feature attribute has active constraints.
static bool validateAttribute(const QgsVectorLayer *layer, const QgsFeature &feature, int attributeIndex, QStringList &errors, QgsFieldConstraints::ConstraintStrength strength=QgsFieldConstraints::ConstraintStrengthNotSet, QgsFieldConstraints::ConstraintOrigin origin=QgsFieldConstraints::ConstraintOriginNotSet)
Tests a feature attribute value to check whether it passes all constraints which are present on the c...
void editCommandStarted(const QString &text)
Signal emitted when a new edit command has been started.
void featuresDeleted(const QgsFeatureIds &fids)
Emitted when features have been deleted.
void attributeDeleted(int idx)
Will be emitted, when an attribute has been deleted from this vector layer.
void editCommandEnded()
Signal emitted, when an edit command successfully ended.
void afterRollBack()
Emitted after changes are rolled back.
void updatedFields()
Emitted whenever the fields available from this layer have been changed.
void beforeRollBack()
Emitted before changes are rolled back.
#define Q_NOWARN_DEPRECATED_POP
Definition qgis.h:8379
QString qgsEnumValueToKey(const T &value, bool *returnOk=nullptr)
Returns the value for the given key of an enum.
Definition qgis.h:8017
#define Q_NOWARN_DEPRECATED_PUSH
Definition qgis.h:8378
QSet< QgsFeatureId > QgsFeatureIds
#define FID_TO_STRING(fid)
qint64 QgsFeatureId
64 bit feature ids negative numbers are used for uncommitted/newly added features
QList< int > QgsAttributeList
Definition qgsfield.h:30
#define QgsDebugMsgLevel(str, level)
Definition qgslogger.h:80
#define QgsDebugError(str)
Definition qgslogger.h:71