QGIS API Documentation 3.28.0-Firenze (ed3ad0430f)
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 "qgslayout.h"
21#include "qgslayoutitemmap.h"
22#include "qgslayoututils.h"
23#include "qgsfeatureiterator.h"
24#include "qgsvectorlayer.h"
25#include "qgslayoutframe.h"
26#include "qgsproject.h"
27#include "qgsrelationmanager.h"
28#include "qgsfieldformatter.h"
30#include "qgsgeometry.h"
31#include "qgsexception.h"
32#include "qgsmapsettings.h"
35#include "qgsgeometryengine.h"
36#include "qgsconditionalstyle.h"
37#include "qgsfontutils.h"
38#include "qgsvariantutils.h"
39
40//
41// QgsLayoutItemAttributeTable
42//
43
45 : QgsLayoutTable( layout )
46{
47 if ( mLayout )
48 {
49 connect( mLayout->project(), static_cast < void ( QgsProject::* )( const QString & ) >( &QgsProject::layerWillBeRemoved ), this, &QgsLayoutItemAttributeTable::removeLayer );
50
51 //coverage layer change = regenerate columns
52 connect( &mLayout->reportContext(), &QgsLayoutReportContext::layerChanged, this, &QgsLayoutItemAttributeTable::atlasLayerChanged );
53 }
55}
56
58{
60}
61
63{
64 return QgsApplication::getThemeIcon( QStringLiteral( "/mLayoutItemTable.svg" ) );
65}
66
68{
70}
71
73{
74 return tr( "<Attribute table frame>" );
75}
76
78{
79 if ( layer == mVectorLayer.get() )
80 {
81 //no change
82 return;
83 }
84
85 QgsVectorLayer *prevLayer = sourceLayer();
86 mVectorLayer.setLayer( layer );
87
88 if ( mSource == QgsLayoutItemAttributeTable::LayerAttributes && layer != prevLayer )
89 {
90 if ( prevLayer )
91 {
92 //disconnect from previous layer
94 }
95
96 //rebuild column list to match all columns from layer
98
99 //listen for modifications to layer and refresh table when they occur
100 connect( mVectorLayer.get(), &QgsVectorLayer::layerModified, this, &QgsLayoutTable::refreshAttributes );
101 }
102
104 emit changed();
105}
106
107void QgsLayoutItemAttributeTable::setRelationId( const QString &relationId )
108{
109 if ( relationId == mRelationId )
110 {
111 //no change
112 return;
113 }
114
115 QgsVectorLayer *prevLayer = sourceLayer();
116 mRelationId = relationId;
117 QgsRelation relation = mLayout->project()->relationManager()->relation( mRelationId );
118 QgsVectorLayer *newLayer = relation.referencingLayer();
119
120 if ( mSource == QgsLayoutItemAttributeTable::RelationChildren && newLayer != prevLayer )
121 {
122 if ( prevLayer )
123 {
124 //disconnect from previous layer
126 }
127
128 //rebuild column list to match all columns from layer
129 resetColumns();
130
131 //listen for modifications to layer and refresh table when they occur
133 }
134
136 emit changed();
137}
138
139void QgsLayoutItemAttributeTable::atlasLayerChanged( QgsVectorLayer *layer )
140{
141 if ( mSource != QgsLayoutItemAttributeTable::AtlasFeature || layer == mCurrentAtlasLayer )
142 {
143 //nothing to do
144 return;
145 }
146
147 //atlas feature mode, atlas layer changed, so we need to reset columns
148 if ( mCurrentAtlasLayer )
149 {
150 //disconnect from previous layer
151 disconnect( mCurrentAtlasLayer, &QgsVectorLayer::layerModified, this, &QgsLayoutTable::refreshAttributes );
152 }
153
154 const bool mustRebuildColumns = static_cast< bool >( mCurrentAtlasLayer ) || mColumns.empty();
155 mCurrentAtlasLayer = layer;
156
157 if ( mustRebuildColumns )
158 {
159 //rebuild column list to match all columns from layer
160 resetColumns();
161 }
162
164
165 //listen for modifications to layer and refresh table when they occur
167}
168
170{
172 if ( !source )
173 {
174 return;
175 }
176
177 //remove existing columns
178 mColumns.clear();
179 mSortColumns.clear();
180
181 //rebuild columns list from vector layer fields
182 int idx = 0;
183 const QgsFields sourceFields = source->fields();
184
185 for ( const auto &field : sourceFields )
186 {
187 QString currentAlias = source->attributeDisplayName( idx );
189 col.setAttribute( field.name() );
190 col.setHeading( currentAlias );
191 mColumns.append( col );
192 idx++;
193 }
194}
195
196void QgsLayoutItemAttributeTable::disconnectCurrentMap()
197{
198 if ( !mMap )
199 {
200 return;
201 }
202
205 disconnect( mMap, &QObject::destroyed, this, &QgsLayoutItemAttributeTable::disconnectCurrentMap );
206 mMap = nullptr;
207}
208
210{
211 return mUseConditionalStyling;
212}
213
215{
216 if ( useConditionalStyling == mUseConditionalStyling )
217 {
218 return;
219 }
220
221 mUseConditionalStyling = useConditionalStyling;
223 emit changed();
224}
225
227{
228 if ( map == mMap )
229 {
230 //no change
231 return;
232 }
233 disconnectCurrentMap();
234
235 mMap = map;
236 if ( mMap )
237 {
238 //listen out for extent changes in linked map
241 connect( mMap, &QObject::destroyed, this, &QgsLayoutItemAttributeTable::disconnectCurrentMap );
242 }
244 emit changed();
245}
246
248{
249 if ( features == mMaximumNumberOfFeatures )
250 {
251 return;
252 }
253
254 mMaximumNumberOfFeatures = features;
256 emit changed();
257}
258
260{
261 if ( uniqueOnly == mShowUniqueRowsOnly )
262 {
263 return;
264 }
265
266 mShowUniqueRowsOnly = uniqueOnly;
268 emit changed();
269}
270
272{
273 if ( visibleOnly == mShowOnlyVisibleFeatures )
274 {
275 return;
276 }
277
278 mShowOnlyVisibleFeatures = visibleOnly;
280 emit changed();
281}
282
284{
285 if ( filterToAtlas == mFilterToAtlasIntersection )
286 {
287 return;
288 }
289
290 mFilterToAtlasIntersection = filterToAtlas;
292 emit changed();
293}
294
296{
297 if ( filter == mFilterFeatures )
298 {
299 return;
300 }
301
302 mFilterFeatures = filter;
304 emit changed();
305}
306
307void QgsLayoutItemAttributeTable::setFeatureFilter( const QString &expression )
308{
309 if ( expression == mFeatureFilter )
310 {
311 return;
312 }
313
314 mFeatureFilter = expression;
316 emit changed();
317}
318
319void QgsLayoutItemAttributeTable::setDisplayedFields( const QStringList &fields, bool refresh )
320{
322 if ( !source )
323 {
324 return;
325 }
326
327 //rebuild columns list, taking only fields contained in supplied list
328 mColumns.clear();
329
330 const QgsFields layerFields = source->fields();
331
332 if ( !fields.isEmpty() )
333 {
334 for ( const QString &field : fields )
335 {
336 int attrIdx = layerFields.lookupField( field );
337 if ( attrIdx < 0 )
338 {
339 continue;
340 }
341 QString currentAlias = source->attributeDisplayName( attrIdx );
343 col.setAttribute( layerFields.at( attrIdx ).name() );
344 col.setHeading( currentAlias );
345 mColumns.append( col );
346 }
347 }
348 else
349 {
350 //resetting, so add all attributes to columns
351 int idx = 0;
352 for ( const QgsField &field : layerFields )
353 {
354 QString currentAlias = source->attributeDisplayName( idx );
356 col.setAttribute( field.name() );
357 col.setHeading( currentAlias );
358 mColumns.append( col );
359 idx++;
360 }
361 }
362
363 if ( refresh )
364 {
366 }
367}
368
369void QgsLayoutItemAttributeTable::restoreFieldAliasMap( const QMap<int, QString> &map )
370{
372 if ( !source )
373 {
374 return;
375 }
376
377 for ( int i = 0; i < mColumns.count(); i++ )
378 {
379 int attrIdx = source->fields().lookupField( mColumns[i].attribute() );
380 if ( map.contains( attrIdx ) )
381 {
382 mColumns[i].setHeading( map.value( attrIdx ) );
383 }
384 else
385 {
386 mColumns[i].setHeading( source->attributeDisplayName( attrIdx ) );
387 }
388 }
389}
390
392{
393 contents.clear();
394 mLayerCache.clear();
395
396 QgsVectorLayer *layer = sourceLayer();
397 if ( !layer )
398 {
399 //no source layer
400 return false;
401 }
402
403 const QgsConditionalLayerStyles *conditionalStyles = layer->conditionalStyles();
404
406 context.setFields( layer->fields() );
407
409 req.setExpressionContext( context );
410
411 //prepare filter expression
412 std::unique_ptr<QgsExpression> filterExpression;
413 bool activeFilter = false;
414 if ( mFilterFeatures && !mFeatureFilter.isEmpty() )
415 {
416 filterExpression = std::make_unique< QgsExpression >( mFeatureFilter );
417 if ( !filterExpression->hasParserError() )
418 {
419 activeFilter = true;
420 req.setFilterExpression( mFeatureFilter );
421 }
422 }
423
424#ifdef HAVE_SERVER_PYTHON_PLUGINS
425 if ( mLayout->renderContext().featureFilterProvider() )
426 {
427 mLayout->renderContext().featureFilterProvider()->filterFeatures( layer, req );
428 }
429#endif
430
431 QgsRectangle selectionRect;
432 QgsGeometry visibleRegion;
433 std::unique_ptr< QgsGeometryEngine > visibleMapEngine;
434 if ( mMap && mShowOnlyVisibleFeatures )
435 {
436 visibleRegion = QgsGeometry::fromQPolygonF( mMap->visibleExtentPolygon() );
437 selectionRect = visibleRegion.boundingBox();
438 //transform back to layer CRS
439 const QgsCoordinateTransform coordTransform( layer->crs(), mMap->crs(), mLayout->project() );
440 QgsCoordinateTransform extentTransform = coordTransform;
441 extentTransform.setBallparkTransformsAreAppropriate( true );
442 try
443 {
444 selectionRect = extentTransform.transformBoundingBox( selectionRect, Qgis::TransformDirection::Reverse );
445 visibleRegion.transform( coordTransform, Qgis::TransformDirection::Reverse );
446 }
447 catch ( QgsCsException &cse )
448 {
449 Q_UNUSED( cse )
450 return false;
451 }
452 visibleMapEngine.reset( QgsGeometry::createGeometryEngine( visibleRegion.constGet() ) );
453 visibleMapEngine->prepareGeometry();
454 }
455
456 QgsGeometry atlasGeometry;
457 std::unique_ptr< QgsGeometryEngine > atlasGeometryEngine;
458 if ( mFilterToAtlasIntersection )
459 {
460 atlasGeometry = mLayout->reportContext().currentGeometry( layer->crs() );
461 if ( !atlasGeometry.isNull() )
462 {
463 if ( selectionRect.isNull() )
464 {
465 selectionRect = atlasGeometry.boundingBox();
466 }
467 else
468 {
469 selectionRect = selectionRect.intersect( atlasGeometry.boundingBox() );
470 }
471
472 atlasGeometryEngine.reset( QgsGeometry::createGeometryEngine( atlasGeometry.constGet() ) );
473 atlasGeometryEngine->prepareGeometry();
474 }
475 }
476
478 {
479 QgsRelation relation = mLayout->project()->relationManager()->relation( mRelationId );
480 QgsFeature atlasFeature = mLayout->reportContext().feature();
481 req = relation.getRelatedFeaturesRequest( atlasFeature );
482 }
483
484 if ( !selectionRect.isEmpty() )
485 req.setFilterRect( selectionRect );
486
488
490 {
491 //source mode is current atlas feature
492 QgsFeature atlasFeature = mLayout->reportContext().feature();
493 req.setFilterFid( atlasFeature.id() );
494 }
495
496 for ( const QgsLayoutTableColumn &column : std::as_const( mSortColumns ) )
497 {
498 req.addOrderBy( column.attribute(), column.sortOrder() == Qt::AscendingOrder );
499 }
500
501 QgsFeature f;
502 int counter = 0;
503 QgsFeatureIterator fit = layer->getFeatures( req );
504
505 mConditionalStyles.clear();
506 mFeatures.clear();
507
508 QVector< QVector< Cell > > tempContents;
509 QgsLayoutTableContents existingContents;
510
511 while ( fit.nextFeature( f ) && counter < mMaximumNumberOfFeatures )
512 {
513 context.setFeature( f );
514 //check feature against filter
515 if ( activeFilter && filterExpression )
516 {
517 QVariant result = filterExpression->evaluate( &context );
518 // skip this feature if the filter evaluation is false
519 if ( !result.toBool() )
520 {
521 continue;
522 }
523 }
524
525 // check against exact map bounds
526 if ( visibleMapEngine )
527 {
528 if ( !f.hasGeometry() )
529 continue;
530
531 if ( !visibleMapEngine->intersects( f.geometry().constGet() ) )
532 continue;
533 }
534
535 //check against atlas feature intersection
536 if ( mFilterToAtlasIntersection )
537 {
538 if ( !f.hasGeometry() || !atlasGeometryEngine )
539 {
540 continue;
541 }
542
543 if ( !atlasGeometryEngine->intersects( f.geometry().constGet() ) )
544 continue;
545 }
546
547 QgsConditionalStyle rowStyle;
548
549 if ( mUseConditionalStyling )
550 {
551 const QList<QgsConditionalStyle> styles = QgsConditionalStyle::matchingConditionalStyles( conditionalStyles->rowStyles(), QVariant(), context );
552 rowStyle = QgsConditionalStyle::compressStyles( styles );
553 }
554
555 // We need to build up two different lists here -- one is a pair of the cell contents along with the cell style.
556 // 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
557 // correctly when this occurs
558 // We also need a list of just the cell contents, so that we can do a quick check for row uniqueness (when the
559 // corresponding option is enabled)
560 QVector< Cell > currentRow;
561#ifdef HAVE_SERVER_PYTHON_PLUGINS
562 mColumns = filteredColumns();
563#endif
564 currentRow.reserve( mColumns.count() );
565 QgsLayoutTableRow rowContents;
566 rowContents.reserve( mColumns.count() );
567
568 for ( const QgsLayoutTableColumn &column : std::as_const( mColumns ) )
569 {
571 int idx = layer->fields().lookupField( column.attribute() );
572 if ( idx != -1 )
573 {
574 QVariant val = f.attributes().at( idx );
575
576 if ( mUseConditionalStyling )
577 {
578 QList<QgsConditionalStyle> styles = conditionalStyles->fieldStyles( layer->fields().at( idx ).name() );
579 styles = QgsConditionalStyle::matchingConditionalStyles( styles, val, context );
580 styles.insert( 0, rowStyle );
581 style = QgsConditionalStyle::compressStyles( styles );
582 }
583
584 const QgsEditorWidgetSetup setup = layer->fields().at( idx ).editorWidgetSetup();
585
586 if ( ! setup.isNull() )
587 {
589 QVariant cache;
590
591 auto it = mLayerCache.constFind( column.attribute() );
592 if ( it != mLayerCache.constEnd() )
593 {
594 cache = it.value();
595 }
596 else
597 {
598 cache = fieldFormatter->createCache( layer, idx, setup.config() );
599 mLayerCache.insert( column.attribute(), cache );
600 }
601
602 val = fieldFormatter->representValue( layer, idx, setup.config(), cache, val );
603 }
604
605 QVariant v = QgsVariantUtils::isNull( val ) ? QString() : replaceWrapChar( val );
606 currentRow << Cell( v, style, f );
607 rowContents << v;
608 }
609 else
610 {
611 // Lets assume it's an expression
612 std::unique_ptr< QgsExpression > expression = std::make_unique< QgsExpression >( column.attribute() );
613 context.lastScope()->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "row_number" ), counter + 1, true ) );
614 expression->prepare( &context );
615 QVariant value = expression->evaluate( &context );
616
617 currentRow << Cell( value, rowStyle, f );
618 rowContents << value;
619 }
620 }
621
622 if ( mShowUniqueRowsOnly )
623 {
624 if ( contentsContainsRow( existingContents, rowContents ) )
625 continue;
626 }
627
628 tempContents << currentRow;
629 existingContents << rowContents;
630 ++counter;
631 }
632
633 // build final table contents
634 contents.reserve( tempContents.size() );
635 mConditionalStyles.reserve( tempContents.size() );
636 mFeatures.reserve( tempContents.size() );
637 for ( auto it = tempContents.constBegin(); it != tempContents.constEnd(); ++it )
638 {
640 QList< QgsConditionalStyle > rowStyles;
641 row.reserve( it->size() );
642 rowStyles.reserve( it->size() );
643
644 for ( auto cellIt = it->constBegin(); cellIt != it->constEnd(); ++cellIt )
645 {
646 row << cellIt->content;
647 rowStyles << cellIt->style;
648 if ( cellIt == it->constBegin() )
649 mFeatures << cellIt->feature;
650 }
651 contents << row;
652 mConditionalStyles << rowStyles;
653 }
654
656 return true;
657}
658
660{
661 if ( row >= mConditionalStyles.size() )
662 return QgsConditionalStyle();
663
664 return mConditionalStyles.at( row ).at( column );
665}
666
668{
670
671 const QgsConditionalStyle style = conditionalCellStyle( row, column );
672 if ( style.isValid() )
673 {
674 // apply conditional style formatting to text format
675 const QFont styleFont = style.font();
676 if ( styleFont != QFont() )
677 {
678 QFont newFont = format.font();
679 // we want to keep all the other font settings, like word/letter spacing
680 newFont.setFamily( styleFont.family() );
681
682 // warning -- there's a potential trap here! We can't just read QFont::styleName(), as that may be blank even when
683 // the font has the bold or italic attributes set! Reading the style name via QFontInfo avoids this and always returns
684 // a correct style name
685 const QString styleName = QgsFontUtils::resolveFontStyleName( styleFont );
686 if ( !styleName.isEmpty() )
687 newFont.setStyleName( styleName );
688
689 newFont.setStrikeOut( styleFont.strikeOut() );
690 newFont.setUnderline( styleFont.underline() );
691 format.setFont( newFont );
692 if ( styleName.isEmpty() )
693 {
694 // we couldn't find a direct match for the conditional font's bold/italic settings as a font style name.
695 // This means the conditional style is using Qt's "faux bold/italic" mode. Even though it causes reduced quality font
696 // rendering, we'll apply it here anyway just to ensure that the rendered font styling matches the conditional style.
697 if ( styleFont.bold() )
698 format.setForcedBold( true );
699 if ( styleFont.italic() )
700 format.setForcedItalic( true );
701 }
702 }
703 }
704
705 return format;
706}
707
709{
710 std::unique_ptr< QgsExpressionContextScope >scope( QgsLayoutTable::scopeForCell( row, column ) );
711 scope->setFeature( mFeatures.value( row ) );
712 scope->setFields( scope->feature().fields() );
713 return scope.release();
714}
715
717{
719
720 if ( mSource == LayerAttributes )
721 {
722 context.appendScope( QgsExpressionContextUtils::layerScope( mVectorLayer.get() ) );
723 }
724
725 return context;
726}
727
729{
731 if ( !mMap && !mMapUuid.isEmpty() && mLayout )
732 {
733 mMap = qobject_cast< QgsLayoutItemMap *>( mLayout->itemByUuid( mMapUuid, true ) );
734 if ( mMap )
735 {
736 //if we have found a valid map item, listen out to extent changes on it and refresh the table
739 }
740 }
741}
742
744{
746
749 {
750 mDataDefinedVectorLayer = nullptr;
751
752 QString currentLayerIdentifier;
753 if ( QgsVectorLayer *currentLayer = mVectorLayer.get() )
754 currentLayerIdentifier = currentLayer->id();
755
756 const QString layerIdentifier = mDataDefinedProperties.valueAsString( QgsLayoutObject::AttributeTableSourceLayer, context, currentLayerIdentifier );
757 QgsVectorLayer *ddLayer = qobject_cast< QgsVectorLayer * >( QgsLayoutUtils::mapLayerFromString( layerIdentifier, mLayout->project() ) );
758 if ( ddLayer )
759 mDataDefinedVectorLayer = ddLayer;
760 }
761
763}
764
765QVariant QgsLayoutItemAttributeTable::replaceWrapChar( const QVariant &variant ) const
766{
767 //avoid converting variants to string if not required (try to maintain original type for sorting)
768 if ( mWrapString.isEmpty() || !variant.toString().contains( mWrapString ) )
769 return variant;
770
771 QString replaced = variant.toString();
772 replaced.replace( mWrapString, QLatin1String( "\n" ) );
773 return replaced;
774}
775
776#ifdef HAVE_SERVER_PYTHON_PLUGINS
777QgsLayoutTableColumns QgsLayoutItemAttributeTable::filteredColumns()
778{
779
780 QgsLayoutTableColumns allowedColumns { mColumns };
781
782 // Filter columns
783 if ( mLayout->renderContext().featureFilterProvider() )
784 {
785
787
788 if ( ! source )
789 {
790 return allowedColumns;
791 }
792
793 QHash<const QString, QSet<QString>> columnAttributesMap;
794 QSet<QString> allowedAttributes;
795
796 for ( const auto &c : std::as_const( allowedColumns ) )
797 {
798 if ( ! c.attribute().isEmpty() && ! columnAttributesMap.contains( c.attribute() ) )
799 {
800 columnAttributesMap[ c.attribute() ] = QSet<QString>();
801 const QgsExpression columnExp { c.attribute() };
802 const auto constRefs { columnExp.findNodes<QgsExpressionNodeColumnRef>() };
803 for ( const auto &cref : constRefs )
804 {
805 columnAttributesMap[ c.attribute() ].insert( cref->name() );
806 allowedAttributes.insert( cref->name() );
807 }
808 }
809 }
810
811 const QStringList filteredAttributes { layout()->renderContext().featureFilterProvider()->layerAttributes( source, allowedAttributes.values() ) };
812 const QSet<QString> filteredAttributesSet( filteredAttributes.constBegin(), filteredAttributes.constEnd() );
813 if ( filteredAttributesSet != allowedAttributes )
814 {
815 const auto forbidden { allowedAttributes.subtract( filteredAttributesSet ) };
816 allowedColumns.erase( std::remove_if( allowedColumns.begin(), allowedColumns.end(), [ &columnAttributesMap, &forbidden ]( QgsLayoutTableColumn & c ) -> bool
817 {
818 for ( const auto &f : std::as_const( forbidden ) )
819 {
820 if ( columnAttributesMap[ c.attribute() ].contains( f ) )
821 {
822 return true;
823 }
824 }
825 return false;
826 } ), allowedColumns.end() );
827
828 }
829 }
830
831 return allowedColumns;
832}
833#endif
834
836{
837 switch ( mSource )
838 {
840 return mLayout->reportContext().layer();
842 {
843 if ( mDataDefinedVectorLayer )
844 return mDataDefinedVectorLayer;
845 else
846 return mVectorLayer.get();
847 }
849 {
850 QgsRelation relation = mLayout->project()->relationManager()->relation( mRelationId );
851 return relation.referencingLayer();
852 }
853 }
854 return nullptr;
855}
856
857void QgsLayoutItemAttributeTable::removeLayer( const QString &layerId )
858{
859 if ( mVectorLayer && mSource == QgsLayoutItemAttributeTable::LayerAttributes )
860 {
861 if ( layerId == mVectorLayer->id() )
862 {
863 mVectorLayer.setLayer( nullptr );
864 //remove existing columns
865 mColumns.clear();
866 }
867 }
868}
869
870void QgsLayoutItemAttributeTable::setWrapString( const QString &wrapString )
871{
872 if ( wrapString == mWrapString )
873 {
874 return;
875 }
876
877 mWrapString = wrapString;
879 emit changed();
880}
881
882bool QgsLayoutItemAttributeTable::writePropertiesToElement( QDomElement &tableElem, QDomDocument &doc, const QgsReadWriteContext &context ) const
883{
884 if ( !QgsLayoutTable::writePropertiesToElement( tableElem, doc, context ) )
885 return false;
886
887 tableElem.setAttribute( QStringLiteral( "source" ), QString::number( static_cast< int >( mSource ) ) );
888 tableElem.setAttribute( QStringLiteral( "relationId" ), mRelationId );
889 tableElem.setAttribute( QStringLiteral( "showUniqueRowsOnly" ), mShowUniqueRowsOnly );
890 tableElem.setAttribute( QStringLiteral( "showOnlyVisibleFeatures" ), mShowOnlyVisibleFeatures );
891 tableElem.setAttribute( QStringLiteral( "filterToAtlasIntersection" ), mFilterToAtlasIntersection );
892 tableElem.setAttribute( QStringLiteral( "maxFeatures" ), mMaximumNumberOfFeatures );
893 tableElem.setAttribute( QStringLiteral( "filterFeatures" ), mFilterFeatures ? QStringLiteral( "true" ) : QStringLiteral( "false" ) );
894 tableElem.setAttribute( QStringLiteral( "featureFilter" ), mFeatureFilter );
895 tableElem.setAttribute( QStringLiteral( "wrapString" ), mWrapString );
896 tableElem.setAttribute( QStringLiteral( "useConditionalStyling" ), mUseConditionalStyling );
897
898 if ( mMap )
899 {
900 tableElem.setAttribute( QStringLiteral( "mapUuid" ), mMap->uuid() );
901 }
902
903 if ( mVectorLayer )
904 {
905 tableElem.setAttribute( QStringLiteral( "vectorLayer" ), mVectorLayer.layerId );
906 tableElem.setAttribute( QStringLiteral( "vectorLayerName" ), mVectorLayer.name );
907 tableElem.setAttribute( QStringLiteral( "vectorLayerSource" ), mVectorLayer.source );
908 tableElem.setAttribute( QStringLiteral( "vectorLayerProvider" ), mVectorLayer.provider );
909 }
910 return true;
911}
912
913bool QgsLayoutItemAttributeTable::readPropertiesFromElement( const QDomElement &itemElem, const QDomDocument &doc, const QgsReadWriteContext &context )
914{
915 if ( QgsVectorLayer *prevLayer = sourceLayer() )
916 {
917 //disconnect from previous layer
919 }
920
921 if ( !QgsLayoutTable::readPropertiesFromElement( itemElem, doc, context ) )
922 return false;
923
924 mSource = QgsLayoutItemAttributeTable::ContentSource( itemElem.attribute( QStringLiteral( "source" ), QStringLiteral( "0" ) ).toInt() );
925 mRelationId = itemElem.attribute( QStringLiteral( "relationId" ), QString() );
926
928 {
929 mCurrentAtlasLayer = mLayout->reportContext().layer();
930 }
931
932 mShowUniqueRowsOnly = itemElem.attribute( QStringLiteral( "showUniqueRowsOnly" ), QStringLiteral( "0" ) ).toInt();
933 mShowOnlyVisibleFeatures = itemElem.attribute( QStringLiteral( "showOnlyVisibleFeatures" ), QStringLiteral( "1" ) ).toInt();
934 mFilterToAtlasIntersection = itemElem.attribute( QStringLiteral( "filterToAtlasIntersection" ), QStringLiteral( "0" ) ).toInt();
935 mFilterFeatures = itemElem.attribute( QStringLiteral( "filterFeatures" ), QStringLiteral( "false" ) ) == QLatin1String( "true" );
936 mFeatureFilter = itemElem.attribute( QStringLiteral( "featureFilter" ), QString() );
937 mMaximumNumberOfFeatures = itemElem.attribute( QStringLiteral( "maxFeatures" ), QStringLiteral( "5" ) ).toInt();
938 mWrapString = itemElem.attribute( QStringLiteral( "wrapString" ) );
939 mUseConditionalStyling = itemElem.attribute( QStringLiteral( "useConditionalStyling" ), QStringLiteral( "0" ) ).toInt();
940
941 //map
942 mMapUuid = itemElem.attribute( QStringLiteral( "mapUuid" ) );
943 if ( mMap )
944 {
947 mMap = nullptr;
948 }
949 // setting new mMap occurs in finalizeRestoreFromXml
950
951 //vector layer
952 QString layerId = itemElem.attribute( QStringLiteral( "vectorLayer" ) );
953 QString layerName = itemElem.attribute( QStringLiteral( "vectorLayerName" ) );
954 QString layerSource = itemElem.attribute( QStringLiteral( "vectorLayerSource" ) );
955 QString layerProvider = itemElem.attribute( QStringLiteral( "vectorLayerProvider" ) );
956 mVectorLayer = QgsVectorLayerRef( layerId, layerName, layerSource, layerProvider );
957 mVectorLayer.resolveWeakly( mLayout->project() );
958
959 //connect to new layer
960 if ( QgsVectorLayer *newLayer = sourceLayer() )
962
964
965 emit changed();
966 return true;
967}
968
970{
971 if ( source == mSource )
972 {
973 return;
974 }
975
976 QgsVectorLayer *prevLayer = sourceLayer();
977 mSource = source;
978 QgsVectorLayer *newLayer = sourceLayer();
979
980 if ( newLayer != prevLayer )
981 {
982 //disconnect from previous layer
983 if ( prevLayer )
984 {
986 }
987
988 //connect to new layer
991 {
992 mCurrentAtlasLayer = newLayer;
993 }
994
995 //layer has changed as a result of the source change, so reset column list
996 resetColumns();
997 }
998
1000 emit changed();
1001}
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.
The QgsConditionalLayerStyles class 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.
Class for doing transforms between two map 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 SIP_THROW(QgsCsException)
Transforms a rectangle from the source CRS to the destination CRS.
Custom exception class for Coordinate Reference System related exceptions.
Definition: qgsexception.h:66
Holder for the widget type and its configuration for a field.
QVariantMap config() const
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 it value from a feature's field.
Class for parsing and evaluation of expressions (formerly called "search strings").
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)
This class wraps a request for features to a vector layer (or directly its vector data provider).
QgsFeatureRequest & setFlags(QgsFeatureRequest::Flags 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.
@ ExactIntersect
Use exact geometry intersection (slower) instead of bounding boxes.
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:56
QgsAttributes attributes
Definition: qgsfeature.h:65
QgsGeometry geometry
Definition: qgsfeature.h:67
bool hasGeometry() const
Returns true if the feature has an associated geometry.
Definition: qgsfeature.cpp:233
Q_GADGET QgsFeatureId id
Definition: qgsfeature.h:64
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:51
QString name
Definition: qgsfield.h:60
QgsEditorWidgetSetup editorWidgetSetup() const
Gets the editor widget setup for the field.
Definition: qgsfield.cpp:631
Container of fields for a vector layer.
Definition: qgsfields.h:45
QgsField at(int i) const
Returns the field at particular index (must be in range 0..N-1).
Definition: qgsfields.cpp:163
int lookupField(const QString &fieldName) const
Looks up field's index from the field name.
Definition: qgsfields.cpp:349
static QString resolveFontStyleName(const QFont &font)
Attempts to resolve the style name corresponding to the specified font object.
A geometry is the spatial representation of a feature.
Definition: qgsgeometry.h:164
const QgsAbstractGeometry * constGet() const SIP_HOLDGIL
Returns a non-modifiable (const) reference to the underlying abstract geometry primitive.
Qgis::GeometryOperationResult transform(const QgsCoordinateTransform &ct, Qgis::TransformDirection direction=Qgis::TransformDirection::Forward, bool transformZ=false) SIP_THROW(QgsCsException)
Transforms this geometry as described by the coordinate transform ct.
static QgsGeometry fromQPolygonF(const QPolygonF &polygon)
Construct geometry from a QPolygonF.
Q_GADGET bool isNull
Definition: qgsgeometry.h:166
static QgsGeometryEngine * createGeometryEngine(const QgsAbstractGeometry *geometry)
Creates and returns a new geometry engine representing the specified geometry.
QgsRectangle boundingBox() const
Returns the bounding box of the geometry.
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.
void refreshDataDefinedProperty(QgsLayoutObject::DataDefinedProperty property=QgsLayoutObject::AllProperties) override
Refreshes a data defined property for the multi frame by reevaluating the property's value and redraw...
QgsExpressionContextScope * scopeForCell(int row, int column) const override
Creates a new QgsExpressionContextScope for the cell at row, column.
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.
QPolygonF visibleExtentPolygon() const
Returns a polygon representing the current visible map extent, considering map extents and rotation.
QgsCoordinateReferenceSystem crs() const
Returns coordinate reference system used for rendering the map.
@ LayoutAttributeTable
Attribute table.
virtual QString uuid() const
Returns the item identification string.
virtual void finalizeRestoreFromXml()
Called after all pending items have been restored from XML.
QgsExpressionContext createExpressionContext() const override
This method needs to be reimplemented in all classes which implement this interface and return an exp...
virtual void refreshDataDefinedProperty(QgsLayoutObject::DataDefinedProperty property=QgsLayoutObject::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 NULL 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.
A class to display a table in the print layout, and allow 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:51
QgsLayoutRenderContext & renderContext()
Returns a reference to the layout's render context, which stores information relating to the current ...
Definition: qgslayout.cpp:359
QgsCoordinateReferenceSystem crs
Definition: qgsmaplayer.h:79
QString id() const
Returns the layer's unique ID, which is used to access this layer from QgsProject.
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:104
void layerWillBeRemoved(const QString &layerId)
Emitted when a layer is about to be removed from the registry.
The class is used as a container of context for various read/write operations on other objects.
A rectangle specified with double values.
Definition: qgsrectangle.h:42
bool isNull() const
Test if the rectangle is null (all coordinates zero or after call to setMinimal()).
Definition: qgsrectangle.h:479
bool isEmpty() const
Returns true if the rectangle is empty.
Definition: qgsrectangle.h:469
QgsRectangle intersect(const QgsRectangle &rect) const
Returns the intersection with the given rectangle.
Definition: qgsrectangle.h:333
QgsVectorLayer * referencingLayer
Definition: qgsrelation.h:46
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.
Definition: qgstextformat.h:41
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)
Returns true if the specified variant should be considered a NULL value.
Represents a vector layer which manages a vector based data sets.
QgsFeatureIterator getFeatures(const QgsFeatureRequest &request=QgsFeatureRequest()) const FINAL
Queries the layer for features specified in request.
QgsFields fields() const FINAL
Returns the list of fields of this layer.
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
const QgsField & field
Definition: qgsfield.h:463
_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.