QGIS API Documentation 3.99.0-Master (a26b91b364d)
qgslayoutitemattributetable.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgslayoutitemattributetable.cpp
3 -------------------------------
4 begin : November 2017
5 copyright : (C) 2017 by Nyall Dawson
6 email : nyall dot dawson at gmail dot com
7 ***************************************************************************/
8
9/***************************************************************************
10 * *
11 * This program is free software; you can redistribute it and/or modify *
12 * it under the terms of the GNU General Public License as published by *
13 * the Free Software Foundation; either version 2 of the License, or *
14 * (at your option) any later version. *
15 * *
16 ***************************************************************************/
17
19#include "moc_qgslayoutitemattributetable.cpp"
20#include "qgslayout.h"
22#include "qgslayoutitemmap.h"
23#include "qgslayoututils.h"
24#include "qgsfeatureiterator.h"
25#include "qgsvectorlayer.h"
26#include "qgslayoutframe.h"
27#include "qgsproject.h"
28#include "qgsrelationmanager.h"
29#include "qgsfieldformatter.h"
31#include "qgsgeometry.h"
32#include "qgsexception.h"
36#include "qgsgeometryengine.h"
37#include "qgsconditionalstyle.h"
38#include "qgsfontutils.h"
39#include "qgsvariantutils.h"
41
42//
43// QgsLayoutItemAttributeTable
44//
45
47 : QgsLayoutTable( layout )
48{
49 if ( mLayout )
50 {
51 connect( mLayout->project(), static_cast < void ( QgsProject::* )( const QString & ) >( &QgsProject::layerWillBeRemoved ), this, &QgsLayoutItemAttributeTable::removeLayer );
52
53 //coverage layer change = regenerate columns
54 connect( &mLayout->reportContext(), &QgsLayoutReportContext::layerChanged, this, &QgsLayoutItemAttributeTable::atlasLayerChanged );
55 }
57}
58
63
65{
66 return QgsApplication::getThemeIcon( QStringLiteral( "/mLayoutItemTable.svg" ) );
67}
68
73
75{
76 return tr( "<Attribute table frame>" );
77}
78
80{
81 if ( layer == mVectorLayer.get() )
82 {
83 //no change
84 return;
85 }
86
87 QgsVectorLayer *prevLayer = sourceLayer();
88 mVectorLayer.setLayer( layer );
89
90 if ( mSource == QgsLayoutItemAttributeTable::LayerAttributes && layer != prevLayer )
91 {
92 if ( prevLayer )
93 {
94 //disconnect from previous layer
96 }
97
98 //rebuild column list to match all columns from layer
100
101 //listen for modifications to layer and refresh table when they occur
102 connect( mVectorLayer.get(), &QgsVectorLayer::layerModified, this, &QgsLayoutTable::refreshAttributes );
103 }
104
106 emit changed();
107}
108
109void QgsLayoutItemAttributeTable::setRelationId( const QString &relationId )
110{
111 if ( relationId == mRelationId )
112 {
113 //no change
114 return;
115 }
116
117 QgsVectorLayer *prevLayer = sourceLayer();
118 mRelationId = relationId;
119 QgsRelation relation = mLayout->project()->relationManager()->relation( mRelationId );
120 QgsVectorLayer *newLayer = relation.referencingLayer();
121
122 if ( mSource == QgsLayoutItemAttributeTable::RelationChildren && newLayer != prevLayer )
123 {
124 if ( prevLayer )
125 {
126 //disconnect from previous layer
128 }
129
130 //rebuild column list to match all columns from layer
131 resetColumns();
132
133 //listen for modifications to layer and refresh table when they occur
135 }
136
138 emit changed();
139}
140
141void QgsLayoutItemAttributeTable::atlasLayerChanged( QgsVectorLayer *layer )
142{
143 if ( mSource != QgsLayoutItemAttributeTable::AtlasFeature || layer == mCurrentAtlasLayer )
144 {
145 //nothing to do
146 return;
147 }
148
149 //atlas feature mode, atlas layer changed, so we need to reset columns
150 if ( mCurrentAtlasLayer )
151 {
152 //disconnect from previous layer
153 disconnect( mCurrentAtlasLayer, &QgsVectorLayer::layerModified, this, &QgsLayoutTable::refreshAttributes );
154 }
155
156 const bool mustRebuildColumns = static_cast< bool >( mCurrentAtlasLayer ) || mColumns.empty();
157 mCurrentAtlasLayer = layer;
158
159 if ( mustRebuildColumns )
160 {
161 //rebuild column list to match all columns from layer
162 resetColumns();
163 }
164
166
167 //listen for modifications to layer and refresh table when they occur
169}
170
172{
174 if ( !source )
175 {
176 return;
177 }
178
179 //remove existing columns
180 mColumns.clear();
181 mSortColumns.clear();
182
183 //rebuild columns list from vector layer fields
184 int idx = 0;
185 const QgsFields sourceFields = source->fields();
186
187 for ( const auto &field : sourceFields )
188 {
189 QString currentAlias = source->attributeDisplayName( idx );
191 col.setAttribute( field.name() );
192 col.setHeading( currentAlias );
193 mColumns.append( col );
194 idx++;
195 }
196}
197
198void QgsLayoutItemAttributeTable::disconnectCurrentMap()
199{
200 if ( !mMap )
201 {
202 return;
203 }
204
207 disconnect( mMap, &QObject::destroyed, this, &QgsLayoutItemAttributeTable::disconnectCurrentMap );
208 mMap = nullptr;
209}
210
212{
213 return mUseConditionalStyling;
214}
215
217{
218 if ( useConditionalStyling == mUseConditionalStyling )
219 {
220 return;
221 }
222
223 mUseConditionalStyling = useConditionalStyling;
225 emit changed();
226}
227
229{
230 if ( map == mMap )
231 {
232 //no change
233 return;
234 }
235 disconnectCurrentMap();
236
237 mMap = map;
238 if ( mMap )
239 {
240 //listen out for extent changes in linked map
243 }
245 emit changed();
246}
247
249{
250 if ( features == mMaximumNumberOfFeatures )
251 {
252 return;
253 }
254
255 mMaximumNumberOfFeatures = features;
257 emit changed();
258}
259
261{
262 if ( uniqueOnly == mShowUniqueRowsOnly )
263 {
264 return;
265 }
266
267 mShowUniqueRowsOnly = uniqueOnly;
269 emit changed();
270}
271
273{
274 if ( visibleOnly == mShowOnlyVisibleFeatures )
275 {
276 return;
277 }
278
279 mShowOnlyVisibleFeatures = visibleOnly;
281 emit changed();
282}
283
285{
286 if ( filterToAtlas == mFilterToAtlasIntersection )
287 {
288 return;
289 }
290
291 mFilterToAtlasIntersection = filterToAtlas;
293 emit changed();
294}
295
297{
298 if ( filter == mFilterFeatures )
299 {
300 return;
301 }
302
303 mFilterFeatures = filter;
305 emit changed();
306}
307
308void QgsLayoutItemAttributeTable::setFeatureFilter( const QString &expression )
309{
310 if ( expression == mFeatureFilter )
311 {
312 return;
313 }
314
315 mFeatureFilter = expression;
317 emit changed();
318}
319
320void QgsLayoutItemAttributeTable::setDisplayedFields( const QStringList &fields, bool refresh )
321{
323 if ( !source )
324 {
325 return;
326 }
327
328 //rebuild columns list, taking only fields contained in supplied list
329 mColumns.clear();
330
331 const QgsFields layerFields = source->fields();
332
333 if ( !fields.isEmpty() )
334 {
335 for ( const QString &field : fields )
336 {
337 int attrIdx = layerFields.lookupField( field );
338 if ( attrIdx < 0 )
339 {
340 continue;
341 }
342 QString currentAlias = source->attributeDisplayName( attrIdx );
344 col.setAttribute( layerFields.at( attrIdx ).name() );
345 col.setHeading( currentAlias );
346 mColumns.append( col );
347 }
348 }
349 else
350 {
351 //resetting, so add all attributes to columns
352 int idx = 0;
353 for ( const QgsField &field : layerFields )
354 {
355 QString currentAlias = source->attributeDisplayName( idx );
357 col.setAttribute( field.name() );
358 col.setHeading( currentAlias );
359 mColumns.append( col );
360 idx++;
361 }
362 }
363
364 if ( refresh )
365 {
367 }
368}
369
370void QgsLayoutItemAttributeTable::restoreFieldAliasMap( const QMap<int, QString> &map )
371{
373 if ( !source )
374 {
375 return;
376 }
377
378 for ( int i = 0; i < mColumns.count(); i++ )
379 {
380 int attrIdx = source->fields().lookupField( mColumns[i].attribute() );
381 if ( map.contains( attrIdx ) )
382 {
383 mColumns[i].setHeading( map.value( attrIdx ) );
384 }
385 else
386 {
387 mColumns[i].setHeading( source->attributeDisplayName( attrIdx ) );
388 }
389 }
390}
391
393{
394 contents.clear();
395 mLayerCache.clear();
396
397 QgsVectorLayer *layer = sourceLayer();
398 if ( !layer )
399 {
400 //no source layer
401 return false;
402 }
403
404 const QgsConditionalLayerStyles *conditionalStyles = layer->conditionalStyles();
405
407 context.setFields( layer->fields() );
408
410 req.setExpressionContext( context );
411
412 //prepare filter expression
413 std::unique_ptr<QgsExpression> filterExpression;
414 bool activeFilter = false;
415 if ( mFilterFeatures && !mFeatureFilter.isEmpty() )
416 {
417 filterExpression = std::make_unique< QgsExpression >( mFeatureFilter );
418 if ( !filterExpression->hasParserError() )
419 {
420 activeFilter = true;
421 req.setFilterExpression( mFeatureFilter );
422 }
423 }
424
425#ifdef HAVE_SERVER_PYTHON_PLUGINS
426 if ( mLayout->renderContext().featureFilterProvider() )
427 {
428 // NOLINTBEGIN(bugprone-branch-clone)
430 if ( mLayout->renderContext().featureFilterProvider()->isFilterThreadSafe() )
431 {
432 mLayout->renderContext().featureFilterProvider()->filterFeatures( layer->id(), req );
433 }
434 else
435 {
436 mLayout->renderContext().featureFilterProvider()->filterFeatures( layer, req );
437 }
439 // NOLINTEND(bugprone-branch-clone)
440 }
441#endif
442
443 QgsRectangle selectionRect;
444 QgsGeometry visibleRegion;
445 std::unique_ptr< QgsGeometryEngine > visibleMapEngine;
446 if ( mMap && mShowOnlyVisibleFeatures )
447 {
448 visibleRegion = QgsGeometry::fromQPolygonF( mMap->visibleExtentPolygon() );
449 selectionRect = visibleRegion.boundingBox();
450 //transform back to layer CRS
451 const QgsCoordinateTransform coordTransform( layer->crs(), mMap->crs(), mLayout->project() );
452 QgsCoordinateTransform extentTransform = coordTransform;
453 extentTransform.setBallparkTransformsAreAppropriate( true );
454 try
455 {
456 selectionRect = extentTransform.transformBoundingBox( selectionRect, Qgis::TransformDirection::Reverse );
457 visibleRegion.transform( coordTransform, Qgis::TransformDirection::Reverse );
458 }
459 catch ( QgsCsException &cse )
460 {
461 Q_UNUSED( cse )
462 return false;
463 }
464 visibleMapEngine.reset( QgsGeometry::createGeometryEngine( visibleRegion.constGet() ) );
465 visibleMapEngine->prepareGeometry();
466 }
467
468 QgsGeometry atlasGeometry;
469 std::unique_ptr< QgsGeometryEngine > atlasGeometryEngine;
470 if ( mFilterToAtlasIntersection )
471 {
472 atlasGeometry = mLayout->reportContext().currentGeometry( layer->crs() );
473 if ( !atlasGeometry.isNull() )
474 {
475 if ( selectionRect.isNull() )
476 {
477 selectionRect = atlasGeometry.boundingBox();
478 }
479 else
480 {
481 selectionRect = selectionRect.intersect( atlasGeometry.boundingBox() );
482 }
483
484 atlasGeometryEngine.reset( QgsGeometry::createGeometryEngine( atlasGeometry.constGet() ) );
485 atlasGeometryEngine->prepareGeometry();
486 }
487 else
488 {
489 return false;
490 }
491 }
492
494 {
495 QgsRelation relation = mLayout->project()->relationManager()->relation( mRelationId );
496 QgsFeature atlasFeature = mLayout->reportContext().feature();
497 req = relation.getRelatedFeaturesRequest( atlasFeature );
498 }
499
500 if ( !selectionRect.isNull() )
501 req.setFilterRect( selectionRect );
502
504
506 {
507 //source mode is current atlas feature
508 QgsFeature atlasFeature = mLayout->reportContext().feature();
509 req.setFilterFid( atlasFeature.id() );
510 }
511
512 for ( const QgsLayoutTableColumn &column : std::as_const( mSortColumns ) )
513 {
514 req.addOrderBy( column.attribute(), column.sortOrder() == Qt::AscendingOrder );
515 }
516
517 QgsFeature f;
518 int counter = 0;
519 QgsFeatureIterator fit = layer->getFeatures( req );
520
521 mConditionalStyles.clear();
522 mFeatures.clear();
523
524 QVector< QVector< Cell > > tempContents;
525 QgsLayoutTableContents existingContents;
526
527 while ( fit.nextFeature( f ) && counter < mMaximumNumberOfFeatures )
528 {
529 context.setFeature( f );
530 //check feature against filter
531 if ( activeFilter && filterExpression )
532 {
533 QVariant result = filterExpression->evaluate( &context );
534 // skip this feature if the filter evaluation is false
535 if ( !result.toBool() )
536 {
537 continue;
538 }
539 }
540
541 // check against exact map bounds
542 if ( visibleMapEngine )
543 {
544 if ( !f.hasGeometry() )
545 continue;
546
547 if ( !visibleMapEngine->intersects( f.geometry().constGet() ) )
548 continue;
549 }
550
551 //check against atlas feature intersection
552 if ( atlasGeometryEngine )
553 {
554 if ( !f.hasGeometry() )
555 {
556 continue;
557 }
558
559 if ( !atlasGeometryEngine->intersects( f.geometry().constGet() ) )
560 continue;
561 }
562
563 QgsConditionalStyle rowStyle;
564
565 if ( mUseConditionalStyling )
566 {
567 const QList<QgsConditionalStyle> styles = QgsConditionalStyle::matchingConditionalStyles( conditionalStyles->rowStyles(), QVariant(), context );
568 rowStyle = QgsConditionalStyle::compressStyles( styles );
569 }
570
571 // We need to build up two different lists here -- one is a pair of the cell contents along with the cell style.
572 // We need this one because we do a sorting step later, and we need to ensure that the cell styling is attached to the right row and sorted
573 // correctly when this occurs
574 // We also need a list of just the cell contents, so that we can do a quick check for row uniqueness (when the
575 // corresponding option is enabled)
576 QVector< Cell > currentRow;
577#ifdef HAVE_SERVER_PYTHON_PLUGINS
578 mColumns = filteredColumns();
579#endif
580 currentRow.reserve( mColumns.count() );
581 QgsLayoutTableRow rowContents;
582 rowContents.reserve( mColumns.count() );
583
584 for ( const QgsLayoutTableColumn &column : std::as_const( mColumns ) )
585 {
587 int idx = layer->fields().lookupField( column.attribute() );
588 if ( idx != -1 )
589 {
590 QVariant val = f.attributes().at( idx );
591
592 if ( mUseConditionalStyling )
593 {
594 QList<QgsConditionalStyle> styles = conditionalStyles->fieldStyles( layer->fields().at( idx ).name() );
595 styles = QgsConditionalStyle::matchingConditionalStyles( styles, val, context );
596 styles.insert( 0, rowStyle );
597 style = QgsConditionalStyle::compressStyles( styles );
598 }
599
600 const QgsEditorWidgetSetup setup = layer->fields().at( idx ).editorWidgetSetup();
601
602 if ( ! setup.isNull() )
603 {
605 QVariant cache;
606
607 auto it = mLayerCache.constFind( column.attribute() );
608 if ( it != mLayerCache.constEnd() )
609 {
610 cache = it.value();
611 }
612 else
613 {
614 cache = fieldFormatter->createCache( layer, idx, setup.config() );
615 mLayerCache.insert( column.attribute(), cache );
616 }
617
618 val = fieldFormatter->representValue( layer, idx, setup.config(), cache, val );
619 }
620
621 QVariant v = QgsVariantUtils::isNull( val ) ? QString() : replaceWrapChar( val );
622 currentRow << Cell( v, style, f );
623 rowContents << v;
624 }
625 else
626 {
627 // Lets assume it's an expression
628 auto expression = std::make_unique< QgsExpression >( column.attribute() );
629 context.lastScope()->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "row_number" ), counter + 1, true ) );
630 expression->prepare( &context );
631 QVariant value = expression->evaluate( &context );
632
633 currentRow << Cell( value, rowStyle, f );
634 rowContents << value;
635 }
636 }
637
638 if ( mShowUniqueRowsOnly )
639 {
640 if ( contentsContainsRow( existingContents, rowContents ) )
641 continue;
642 }
643
644 tempContents << currentRow;
645 existingContents << rowContents;
646 ++counter;
647 }
648
649 // build final table contents
650 contents.reserve( tempContents.size() );
651 mConditionalStyles.reserve( tempContents.size() );
652 mFeatures.reserve( tempContents.size() );
653 for ( auto it = tempContents.constBegin(); it != tempContents.constEnd(); ++it )
654 {
656 QList< QgsConditionalStyle > rowStyles;
657 row.reserve( it->size() );
658 rowStyles.reserve( it->size() );
659
660 for ( auto cellIt = it->constBegin(); cellIt != it->constEnd(); ++cellIt )
661 {
662 row << cellIt->content;
663 rowStyles << cellIt->style;
664 if ( cellIt == it->constBegin() )
665 mFeatures << cellIt->feature;
666 }
667 contents << row;
668 mConditionalStyles << rowStyles;
669 }
670
672 return true;
673}
674
676{
677 if ( row >= mConditionalStyles.size() )
678 return QgsConditionalStyle();
679
680 return mConditionalStyles.at( row ).at( column );
681}
682
684{
686
687 const QgsConditionalStyle style = conditionalCellStyle( row, column );
688 if ( style.isValid() )
689 {
690 // apply conditional style formatting to text format
691 const QFont styleFont = style.font();
692 if ( styleFont != QFont() )
693 {
694 QFont newFont = format.font();
695 // we want to keep all the other font settings, like word/letter spacing
696 QgsFontUtils::setFontFamily( newFont, styleFont.family() );
697
698 // warning -- there's a potential trap here! We can't just read QFont::styleName(), as that may be blank even when
699 // the font has the bold or italic attributes set! Reading the style name via QFontInfo avoids this and always returns
700 // a correct style name
701 const QString styleName = QgsFontUtils::resolveFontStyleName( styleFont );
702 if ( !styleName.isEmpty() )
703 newFont.setStyleName( styleName );
704
705 newFont.setStrikeOut( styleFont.strikeOut() );
706 newFont.setUnderline( styleFont.underline() );
707 format.setFont( newFont );
708 if ( styleName.isEmpty() )
709 {
710 // we couldn't find a direct match for the conditional font's bold/italic settings as a font style name.
711 // This means the conditional style is using Qt's "faux bold/italic" mode. Even though it causes reduced quality font
712 // rendering, we'll apply it here anyway just to ensure that the rendered font styling matches the conditional style.
713 if ( styleFont.bold() )
714 format.setForcedBold( true );
715 if ( styleFont.italic() )
716 format.setForcedItalic( true );
717 }
718 }
719 }
720
721 return format;
722}
723
725{
726 std::unique_ptr< QgsExpressionContextScope >scope( QgsLayoutTable::scopeForCell( row, column ) );
727 scope->setFeature( mFeatures.value( row ) );
728 scope->setFields( scope->feature().fields() );
729 return scope.release();
730}
731
733{
735
736 if ( mSource == LayerAttributes )
737 {
738 context.appendScope( QgsExpressionContextUtils::layerScope( mVectorLayer.get() ) );
739 }
740
741 return context;
742}
743
745{
747 if ( !mMap && !mMapUuid.isEmpty() && mLayout )
748 {
749 mMap = qobject_cast< QgsLayoutItemMap *>( mLayout->itemByUuid( mMapUuid, true ) );
750 if ( mMap )
751 {
752 //if we have found a valid map item, listen out to extent changes on it and refresh the table
755 }
756 }
757}
758
760{
762
765 {
766 mDataDefinedVectorLayer = nullptr;
767
768 QString currentLayerIdentifier;
769 if ( QgsVectorLayer *currentLayer = mVectorLayer.get() )
770 currentLayerIdentifier = currentLayer->id();
771
772 const QString layerIdentifier = mDataDefinedProperties.valueAsString( QgsLayoutObject::DataDefinedProperty::AttributeTableSourceLayer, context, currentLayerIdentifier );
773 QgsVectorLayer *ddLayer = qobject_cast< QgsVectorLayer * >( QgsLayoutUtils::mapLayerFromString( layerIdentifier, mLayout->project() ) );
774 if ( ddLayer )
775 mDataDefinedVectorLayer = ddLayer;
776 }
777
779}
780
781QVariant QgsLayoutItemAttributeTable::replaceWrapChar( const QVariant &variant ) const
782{
783 //avoid converting variants to string if not required (try to maintain original type for sorting)
784 if ( mWrapString.isEmpty() || !variant.toString().contains( mWrapString ) )
785 return variant;
786
787 QString replaced = variant.toString();
788 replaced.replace( mWrapString, QLatin1String( "\n" ) );
789 return replaced;
790}
791
792#ifdef HAVE_SERVER_PYTHON_PLUGINS
793QgsLayoutTableColumns QgsLayoutItemAttributeTable::filteredColumns()
794{
795
796 QgsLayoutTableColumns allowedColumns { mColumns };
797
798 // Filter columns
799 if ( mLayout->renderContext().featureFilterProvider() )
800 {
801
803
804 if ( ! source )
805 {
806 return allowedColumns;
807 }
808
809 QHash<const QString, QSet<QString>> columnAttributesMap;
810 QSet<QString> allowedAttributes;
811
812 for ( const auto &c : std::as_const( allowedColumns ) )
813 {
814 if ( ! c.attribute().isEmpty() && ! columnAttributesMap.contains( c.attribute() ) )
815 {
816 columnAttributesMap[ c.attribute() ] = QSet<QString>();
817 const QgsExpression columnExp { c.attribute() };
818 const auto constRefs { columnExp.findNodes<QgsExpressionNodeColumnRef>() };
819 for ( const auto &cref : constRefs )
820 {
821 columnAttributesMap[ c.attribute() ].insert( cref->name() );
822 allowedAttributes.insert( cref->name() );
823 }
824 }
825 }
826
827 const QStringList filteredAttributes { layout()->renderContext().featureFilterProvider()->layerAttributes( source, allowedAttributes.values() ) };
828 const QSet<QString> filteredAttributesSet( filteredAttributes.constBegin(), filteredAttributes.constEnd() );
829 if ( filteredAttributesSet != allowedAttributes )
830 {
831 const auto forbidden { allowedAttributes.subtract( filteredAttributesSet ) };
832 allowedColumns.erase( std::remove_if( allowedColumns.begin(), allowedColumns.end(), [ &columnAttributesMap, &forbidden ]( QgsLayoutTableColumn & c ) -> bool
833 {
834 for ( const auto &f : std::as_const( forbidden ) )
835 {
836 if ( columnAttributesMap[ c.attribute() ].contains( f ) )
837 {
838 return true;
839 }
840 }
841 return false;
842 } ), allowedColumns.end() );
843
844 }
845 }
846
847 return allowedColumns;
848}
849#endif
850
852{
853 switch ( mSource )
854 {
856 return mLayout->reportContext().layer();
858 {
859 if ( mDataDefinedVectorLayer )
860 return mDataDefinedVectorLayer;
861 else
862 return mVectorLayer.get();
863 }
865 {
866 QgsRelation relation = mLayout->project()->relationManager()->relation( mRelationId );
867 return relation.referencingLayer();
868 }
869 }
870 return nullptr;
871}
872
873void QgsLayoutItemAttributeTable::removeLayer( const QString &layerId )
874{
875 if ( mVectorLayer && mSource == QgsLayoutItemAttributeTable::LayerAttributes )
876 {
877 if ( layerId == mVectorLayer->id() )
878 {
879 mVectorLayer.setLayer( nullptr );
880 //remove existing columns
881 mColumns.clear();
882 }
883 }
884}
885
886void QgsLayoutItemAttributeTable::setWrapString( const QString &wrapString )
887{
888 if ( wrapString == mWrapString )
889 {
890 return;
891 }
892
893 mWrapString = wrapString;
895 emit changed();
896}
897
898bool QgsLayoutItemAttributeTable::writePropertiesToElement( QDomElement &tableElem, QDomDocument &doc, const QgsReadWriteContext &context ) const
899{
900 if ( !QgsLayoutTable::writePropertiesToElement( tableElem, doc, context ) )
901 return false;
902
903 tableElem.setAttribute( QStringLiteral( "source" ), QString::number( static_cast< int >( mSource ) ) );
904 tableElem.setAttribute( QStringLiteral( "relationId" ), mRelationId );
905 tableElem.setAttribute( QStringLiteral( "showUniqueRowsOnly" ), mShowUniqueRowsOnly );
906 tableElem.setAttribute( QStringLiteral( "showOnlyVisibleFeatures" ), mShowOnlyVisibleFeatures );
907 tableElem.setAttribute( QStringLiteral( "filterToAtlasIntersection" ), mFilterToAtlasIntersection );
908 tableElem.setAttribute( QStringLiteral( "maxFeatures" ), mMaximumNumberOfFeatures );
909 tableElem.setAttribute( QStringLiteral( "filterFeatures" ), mFilterFeatures ? QStringLiteral( "true" ) : QStringLiteral( "false" ) );
910 tableElem.setAttribute( QStringLiteral( "featureFilter" ), mFeatureFilter );
911 tableElem.setAttribute( QStringLiteral( "wrapString" ), mWrapString );
912 tableElem.setAttribute( QStringLiteral( "useConditionalStyling" ), mUseConditionalStyling );
913
914 if ( mMap )
915 {
916 tableElem.setAttribute( QStringLiteral( "mapUuid" ), mMap->uuid() );
917 }
918
919 if ( mVectorLayer )
920 {
921 tableElem.setAttribute( QStringLiteral( "vectorLayer" ), mVectorLayer.layerId );
922 tableElem.setAttribute( QStringLiteral( "vectorLayerName" ), mVectorLayer.name );
923 tableElem.setAttribute( QStringLiteral( "vectorLayerSource" ), mVectorLayer.source );
924 tableElem.setAttribute( QStringLiteral( "vectorLayerProvider" ), mVectorLayer.provider );
925 }
926 return true;
927}
928
929bool QgsLayoutItemAttributeTable::readPropertiesFromElement( const QDomElement &itemElem, const QDomDocument &doc, const QgsReadWriteContext &context )
930{
931 if ( QgsVectorLayer *prevLayer = sourceLayer() )
932 {
933 //disconnect from previous layer
935 }
936
937 if ( !QgsLayoutTable::readPropertiesFromElement( itemElem, doc, context ) )
938 return false;
939
940 mSource = QgsLayoutItemAttributeTable::ContentSource( itemElem.attribute( QStringLiteral( "source" ), QStringLiteral( "0" ) ).toInt() );
941 mRelationId = itemElem.attribute( QStringLiteral( "relationId" ), QString() );
942
944 {
945 mCurrentAtlasLayer = mLayout->reportContext().layer();
946 }
947
948 mShowUniqueRowsOnly = itemElem.attribute( QStringLiteral( "showUniqueRowsOnly" ), QStringLiteral( "0" ) ).toInt();
949 mShowOnlyVisibleFeatures = itemElem.attribute( QStringLiteral( "showOnlyVisibleFeatures" ), QStringLiteral( "1" ) ).toInt();
950 mFilterToAtlasIntersection = itemElem.attribute( QStringLiteral( "filterToAtlasIntersection" ), QStringLiteral( "0" ) ).toInt();
951 mFilterFeatures = itemElem.attribute( QStringLiteral( "filterFeatures" ), QStringLiteral( "false" ) ) == QLatin1String( "true" );
952 mFeatureFilter = itemElem.attribute( QStringLiteral( "featureFilter" ), QString() );
953 mMaximumNumberOfFeatures = itemElem.attribute( QStringLiteral( "maxFeatures" ), QStringLiteral( "5" ) ).toInt();
954 mWrapString = itemElem.attribute( QStringLiteral( "wrapString" ) );
955 mUseConditionalStyling = itemElem.attribute( QStringLiteral( "useConditionalStyling" ), QStringLiteral( "0" ) ).toInt();
956
957 //map
958 mMapUuid = itemElem.attribute( QStringLiteral( "mapUuid" ) );
959 if ( mMap )
960 {
963 mMap = nullptr;
964 }
965 // setting new mMap occurs in finalizeRestoreFromXml
966
967 //vector layer
968 QString layerId = itemElem.attribute( QStringLiteral( "vectorLayer" ) );
969 QString layerName = itemElem.attribute( QStringLiteral( "vectorLayerName" ) );
970 QString layerSource = itemElem.attribute( QStringLiteral( "vectorLayerSource" ) );
971 QString layerProvider = itemElem.attribute( QStringLiteral( "vectorLayerProvider" ) );
972 mVectorLayer = QgsVectorLayerRef( layerId, layerName, layerSource, layerProvider );
973 mVectorLayer.resolveWeakly( mLayout->project() );
974
975 //connect to new layer
976 if ( QgsVectorLayer *newLayer = sourceLayer() )
978
980
981 emit changed();
982 return true;
983}
984
986{
987 if ( source == mSource )
988 {
989 return;
990 }
991
992 QgsVectorLayer *prevLayer = sourceLayer();
993 mSource = source;
994 QgsVectorLayer *newLayer = sourceLayer();
995
996 if ( newLayer != prevLayer )
997 {
998 //disconnect from previous layer
999 if ( prevLayer )
1000 {
1001 disconnect( prevLayer, &QgsVectorLayer::layerModified, this, &QgsLayoutTable::refreshAttributes );
1002 }
1003
1004 //connect to new layer
1007 {
1008 mCurrentAtlasLayer = newLayer;
1009 }
1010
1011 //layer has changed as a result of the source change, so reset column list
1012 resetColumns();
1013 }
1014
1016 emit changed();
1017}
@ ExactIntersect
Use exact geometry intersection (slower) instead of bounding boxes.
@ NoFlags
No flags are set.
@ Reverse
Reverse/inverse transform (from destination to source)
QString valueAsString(int key, const QgsExpressionContext &context, const QString &defaultString=QString(), bool *ok=nullptr) const
Calculates the current value of the property with the specified key and interprets it as a string.
static QIcon getThemeIcon(const QString &name, const QColor &fillColor=QColor(), const QColor &strokeColor=QColor())
Helper to get a theme icon.
static QgsFieldFormatterRegistry * fieldFormatterRegistry()
Gets the registry of available field formatters.
Holds conditional style information for a layer.
QgsConditionalStyles rowStyles() const
Returns a list of row styles associated with the layer.
QList< QgsConditionalStyle > fieldStyles(const QString &fieldName) const
Returns the conditional styles set for the field with matching fieldName.
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.
QFont font() const
The font for the style.
bool isValid() const
isValid Check if this rule is valid.
Handles coordinate transforms between two coordinate systems.
void setBallparkTransformsAreAppropriate(bool appropriate)
Sets whether approximate "ballpark" results are appropriate for this coordinate transform.
QgsRectangle transformBoundingBox(const QgsRectangle &rectangle, Qgis::TransformDirection direction=Qgis::TransformDirection::Forward, bool handle180Crossover=false) const
Transforms a rectangle from the source CRS to the destination CRS.
Custom exception class for Coordinate Reference System related exceptions.
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.
bool isNull() const
Returns true if there is no widget configured.
virtual QgsExpressionContext createExpressionContext() const =0
This method needs to be reimplemented in all classes which implement this interface and return an exp...
Single scope for storing variables and functions for use within a QgsExpressionContext.
void addVariable(const QgsExpressionContextScope::StaticVariable &variable)
Adds a variable into the context scope.
static QgsExpressionContextScope * layerScope(const QgsMapLayer *layer)
Creates a new scope which contains variables and functions relating to a QgsMapLayer.
Expression contexts are used to encapsulate the parameters around which a QgsExpression should be eva...
QgsExpressionContextScope * lastScope()
Returns the last scope added to the context.
void appendScope(QgsExpressionContextScope *scope)
Appends a scope to the end of the context.
void setFeature(const QgsFeature &feature)
Convenience function for setting a feature for the context.
void setFields(const QgsFields &fields)
Convenience function for setting a fields for the context.
An expression node which takes its value from a feature's field.
Handles parsing and evaluation of expressions (formerly called "search strings").
QList< const T * > findNodes() const
Returns a list of all nodes of the given class which are used in this expression.
virtual QStringList layerAttributes(const QgsVectorLayer *layer, const QStringList &attributes) const =0
Returns the list of visible attribute names from a list of attributes names for the given layer.
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.
QgsFeatureRequest & addOrderBy(const QString &expression, bool ascending=true)
Adds a new OrderByClause, appending it as the least important one.
QgsFeatureRequest & setFilterExpression(const QString &expression)
Set the filter expression.
QgsFeatureRequest & setExpressionContext(const QgsExpressionContext &context)
Sets the expression context used to evaluate filter expressions.
QgsFeatureRequest & setFilterFid(QgsFeatureId fid)
Sets the feature ID that should be fetched.
QgsFeatureRequest & setFilterRect(const QgsRectangle &rectangle)
Sets the rectangle from which features will be taken.
The feature class encapsulates a single feature including its unique ID, geometry and a list of field...
Definition qgsfeature.h:58
QgsAttributes attributes
Definition qgsfeature.h:67
QgsFeatureId id
Definition qgsfeature.h:66
QgsGeometry geometry
Definition qgsfeature.h:69
bool hasGeometry() const
Returns true if the feature has an associated geometry.
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 QVariant createCache(QgsVectorLayer *layer, int fieldIndex, const QVariantMap &config) const
Create a cache for a given field.
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.
Encapsulate a field in an attribute table or data source.
Definition qgsfield.h:53
QString name
Definition qgsfield.h:62
QgsEditorWidgetSetup editorWidgetSetup() const
Gets the editor widget setup for the field.
Definition qgsfield.cpp:746
Container of fields for a vector layer.
Definition qgsfields.h:46
QgsField at(int i) const
Returns the field at particular index (must be in range 0..N-1).
Q_INVOKABLE int lookupField(const QString &fieldName) const
Looks up field's index from the field name.
static QString resolveFontStyleName(const QFont &font)
Attempts to resolve the style name corresponding to the specified font object.
static void setFontFamily(QFont &font, const QString &family)
Sets the family for a font object.
A geometry is the spatial representation of a feature.
static QgsGeometry fromQPolygonF(const QPolygonF &polygon)
Construct geometry from a QPolygonF.
Qgis::GeometryOperationResult transform(const QgsCoordinateTransform &ct, Qgis::TransformDirection direction=Qgis::TransformDirection::Forward, bool transformZ=false)
Transforms this geometry as described by the coordinate transform ct.
const QgsAbstractGeometry * constGet() const
Returns a non-modifiable (const) reference to the underlying abstract geometry primitive.
QgsRectangle boundingBox() const
Returns the bounding box of the geometry.
static QgsGeometryEngine * createGeometryEngine(const QgsAbstractGeometry *geometry, double precision=0.0, Qgis::GeosCreationFlags flags=Qgis::GeosCreationFlag::SkipEmptyInteriorRings)
Creates and returns a new geometry engine representing the specified geometry using precision on a gr...
A layout table subclass that displays attributes from a vector layer.
void resetColumns()
Resets the attribute table's columns to match the vector layer's fields.
QString wrapString() const
Returns the string used to wrap the contents of the table cells by.
bool readPropertiesFromElement(const QDomElement &itemElem, const QDomDocument &doc, const QgsReadWriteContext &context) override
Sets multiframe state from a DOM element.
ContentSource
Specifies the content source for the attribute table.
@ AtlasFeature
Table shows attributes from the current atlas feature.
@ RelationChildren
Table shows attributes from related child features.
@ LayerAttributes
Table shows attributes from features in a vector layer.
QgsVectorLayer * sourceLayer() const
Returns the source layer for the table, considering the table source mode.
void setDisplayedFields(const QStringList &fields, bool refresh=true)
Sets the attributes to display in the table.
void setUseConditionalStyling(bool enabled)
Sets whether the attribute table will be rendered using the conditional styling properties of the lin...
void setRelationId(const QString &id)
Sets the relation id from which to display child features.
void setMaximumNumberOfFeatures(int features)
Sets the maximum number of features shown by the table.
void setDisplayOnlyVisibleFeatures(bool visibleOnly)
Sets the attribute table to only show features which are visible in a map item.
void setFeatureFilter(const QString &expression)
Sets the expression used for filtering features in the table.
QgsExpressionContext createExpressionContext() const override
This method needs to be reimplemented in all classes which implement this interface and return an exp...
void finalizeRestoreFromXml() override
Called after all pending items have been restored from XML.
bool useConditionalStyling() const
Returns true if the attribute table will be rendered using the conditional styling properties of the ...
ContentSource source() const
Returns the source for attributes shown in the table body.
int type() const override
Returns unique multiframe type id.
QgsConditionalStyle conditionalCellStyle(int row, int column) const override
Returns the conditional style to use for the cell at row, column.
QgsExpressionContextScope * scopeForCell(int row, int column) const override
Creates a new QgsExpressionContextScope for the cell at row, column.
void refreshDataDefinedProperty(QgsLayoutObject::DataDefinedProperty property=QgsLayoutObject::DataDefinedProperty::AllProperties) override
Refreshes a data defined property for the multi frame by reevaluating the property's value and redraw...
void setFilterFeatures(bool filter)
Sets whether the feature filter is active for the attribute table.
QgsLayoutItemMap * map() const
Returns the layout map whose extents are controlling the features shown in the table.
void setUniqueRowsOnly(bool uniqueOnly)
Sets attribute table to only show unique rows.
QString relationId() const
Returns the relation id which the table displays child features from.
void setWrapString(const QString &wrapString)
Sets a string to wrap the contents of the table cells by.
QIcon icon() const override
Returns the item's icon.
void setMap(QgsLayoutItemMap *map)
Sets a layout map to use to limit the extent of features shown in the attribute table.
void setFilterToAtlasFeature(bool filterToAtlas)
Sets attribute table to only show features which intersect the current atlas feature.
QString displayName() const override
Returns the multiframe display name.
bool writePropertiesToElement(QDomElement &elem, QDomDocument &doc, const QgsReadWriteContext &context) const override
Stores multiframe state within an XML DOM element.
bool getTableContents(QgsLayoutTableContents &contents) override
Queries the attribute table's vector layer for attributes to show in the table.
QgsLayoutItemAttributeTable(QgsLayout *layout)
Constructor for QgsLayoutItemAttributeTable, attached to the specified layout.
QgsTextFormat textFormatForCell(int row, int column) const override
Returns the text format to use for the cell at the specified row and column.
static QgsLayoutItemAttributeTable * create(QgsLayout *layout)
Returns a new QgsLayoutItemAttributeTable for the specified parent layout.
void setVectorLayer(QgsVectorLayer *layer)
Sets the vector layer from which to display feature attributes.
void setSource(ContentSource source)
Sets the source for attributes to show in table body.
Layout graphical items for displaying a map.
void extentChanged()
Emitted when the map's extent changes.
void mapRotationChanged(double newRotation)
Emitted when the map's rotation changes.
@ LayoutAttributeTable
Attribute table.
virtual void finalizeRestoreFromXml()
Called after all pending items have been restored from XML.
virtual void refreshDataDefinedProperty(QgsLayoutObject::DataDefinedProperty property=QgsLayoutObject::DataDefinedProperty::AllProperties)
Refreshes a data defined property for the multi frame by reevaluating the property's value and redraw...
QgsPropertyCollection mDataDefinedProperties
const QgsLayout * layout() const
Returns the layout the object is attached to.
void changed()
Emitted when the object's properties change.
QPointer< QgsLayout > mLayout
DataDefinedProperty
Data defined properties for different item types.
@ AttributeTableSourceLayer
Attribute table source layer.
@ AllProperties
All properties for item.
QgsFeatureFilterProvider * featureFilterProvider() const
Returns the (possibly nullptr) feature filter provider.
void layerChanged(QgsVectorLayer *layer)
Emitted when the context's layer is changed.
Stores properties of a column for a QgsLayoutTable.
void setAttribute(const QString &attribute)
Sets the attribute name or expression used for the column's values.
void setHeading(const QString &heading)
Sets the heading for a column, which is the value displayed in the column's header cell.
Displays a table in the print layout, and allows the table to span over multiple frames.
virtual void refreshAttributes()
Refreshes the contents shown in the table by querying for new data.
void recalculateTableSize()
Recalculates and updates the size of the table and all table frames.
virtual QgsExpressionContextScope * scopeForCell(int row, int column) const
Creates a new QgsExpressionContextScope for the cell at row, column.
bool contentsContainsRow(const QgsLayoutTableContents &contents, const QgsLayoutTableRow &row) const
Checks whether a table contents contains a given row.
QgsLayoutTableColumns mColumns
Columns to show in table.
QgsTextFormat mContentTextFormat
bool writePropertiesToElement(QDomElement &elem, QDomDocument &doc, const QgsReadWriteContext &context) const override
Stores multiframe state within an XML DOM element.
QgsLayoutTableSortColumns mSortColumns
Columns to sort the table.
bool readPropertiesFromElement(const QDomElement &itemElem, const QDomDocument &doc, const QgsReadWriteContext &context) override
Sets multiframe state from a DOM element.
QgsLayoutTableContents & contents()
Returns the current contents of the table.
void refresh() override
static QgsMapLayer * mapLayerFromString(const QString &string, QgsProject *project)
Resolves a string into a map layer from a given project.
Base class for layouts, which can contain items such as maps, labels, scalebars, etc.
Definition qgslayout.h:49
QgsLayoutRenderContext & renderContext()
Returns a reference to the layout's render context, which stores information relating to the current ...
QgsCoordinateReferenceSystem crs
Definition qgsmaplayer.h:85
QString id
Definition qgsmaplayer.h:81
void layerModified()
Emitted when modifications has been done on layer.
Encapsulates a QGIS project, including sets of map layers and their styles, layouts,...
Definition qgsproject.h:107
void layerWillBeRemoved(const QString &layerId)
Emitted when a layer is about to be removed from the registry.
A container for the context for various read/write operations on objects.
A rectangle specified with double values.
QgsRectangle intersect(const QgsRectangle &rect) const
Returns the intersection with the given rectangle.
Represents a relationship between two vector layers.
Definition qgsrelation.h:44
QgsVectorLayer * referencingLayer
Definition qgsrelation.h:48
QgsFeatureRequest getRelatedFeaturesRequest(const QgsFeature &feature) const
Creates a request to return all the features on the referencing (child) layer which have a foreign ke...
Container for all settings relating to text rendering.
void setFont(const QFont &font)
Sets the font used for rendering text.
void setForcedItalic(bool forced)
Sets whether the format is set to force an italic style.
void setForcedBold(bool forced)
Sets whether the format is set to force a bold style.
QFont font() const
Returns the font used for rendering text.
static bool isNull(const QVariant &variant, bool silenceNullWarnings=false)
Returns true if the specified variant should be considered a NULL value.
Represents a vector layer which manages a vector based dataset.
QgsFeatureIterator getFeatures(const QgsFeatureRequest &request=QgsFeatureRequest()) const FINAL
Queries the layer for features specified in request.
QgsConditionalLayerStyles * conditionalStyles() const
Returns the conditional styles that are set for this layer.
QVector< QgsLayoutTableColumn > QgsLayoutTableColumns
List of column definitions for a QgsLayoutTable.
QVector< QgsLayoutTableRow > QgsLayoutTableContents
List of QgsLayoutTableRows, representing rows and column cell contents for a QgsLayoutTable.
QVector< QVariant > QgsLayoutTableRow
List of QVariants, representing a the contents of a single row in a QgsLayoutTable.
As part of the API refactoring and improvements which landed in the Processing API was substantially reworked from the x version This was done in order to allow much of the underlying Processing framework to be ported into c
#define Q_NOWARN_DEPRECATED_POP
Definition qgis.h:6945
#define Q_NOWARN_DEPRECATED_PUSH
Definition qgis.h:6944
_LayerRef< QgsVectorLayer > QgsVectorLayerRef
Single variable definition for use within a QgsExpressionContextScope.
TYPE * resolveWeakly(const QgsProject *project, MatchType matchType=MatchType::All)
Resolves the map layer by attempting to find a matching layer in a project using a weak match.
QString source
Weak reference to layer public source.
QString name
Weak reference to layer name.
TYPE * get() const
Returns a pointer to the layer, or nullptr if the reference has not yet been matched to a layer.
QString provider
Weak reference to layer provider.
void setLayer(TYPE *l)
Sets the reference to point to a specified layer.
QString layerId
Original layer ID.