QGIS API Documentation 3.30.0-'s-Hertogenbosch (f186b8efe0)
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"
35#include "qgsgeometryengine.h"
36#include "qgsconditionalstyle.h"
37#include "qgsfontutils.h"
38#include "qgsvariantutils.h"
40
41//
42// QgsLayoutItemAttributeTable
43//
44
46 : QgsLayoutTable( layout )
47{
48 if ( mLayout )
49 {
50 connect( mLayout->project(), static_cast < void ( QgsProject::* )( const QString & ) >( &QgsProject::layerWillBeRemoved ), this, &QgsLayoutItemAttributeTable::removeLayer );
51
52 //coverage layer change = regenerate columns
53 connect( &mLayout->reportContext(), &QgsLayoutReportContext::layerChanged, this, &QgsLayoutItemAttributeTable::atlasLayerChanged );
54 }
56}
57
59{
61}
62
64{
65 return QgsApplication::getThemeIcon( QStringLiteral( "/mLayoutItemTable.svg" ) );
66}
67
69{
71}
72
74{
75 return tr( "<Attribute table frame>" );
76}
77
79{
80 if ( layer == mVectorLayer.get() )
81 {
82 //no change
83 return;
84 }
85
86 QgsVectorLayer *prevLayer = sourceLayer();
87 mVectorLayer.setLayer( layer );
88
89 if ( mSource == QgsLayoutItemAttributeTable::LayerAttributes && layer != prevLayer )
90 {
91 if ( prevLayer )
92 {
93 //disconnect from previous layer
95 }
96
97 //rebuild column list to match all columns from layer
99
100 //listen for modifications to layer and refresh table when they occur
101 connect( mVectorLayer.get(), &QgsVectorLayer::layerModified, this, &QgsLayoutTable::refreshAttributes );
102 }
103
105 emit changed();
106}
107
108void QgsLayoutItemAttributeTable::setRelationId( const QString &relationId )
109{
110 if ( relationId == mRelationId )
111 {
112 //no change
113 return;
114 }
115
116 QgsVectorLayer *prevLayer = sourceLayer();
117 mRelationId = relationId;
118 QgsRelation relation = mLayout->project()->relationManager()->relation( mRelationId );
119 QgsVectorLayer *newLayer = relation.referencingLayer();
120
121 if ( mSource == QgsLayoutItemAttributeTable::RelationChildren && newLayer != prevLayer )
122 {
123 if ( prevLayer )
124 {
125 //disconnect from previous layer
127 }
128
129 //rebuild column list to match all columns from layer
130 resetColumns();
131
132 //listen for modifications to layer and refresh table when they occur
134 }
135
137 emit changed();
138}
139
140void QgsLayoutItemAttributeTable::atlasLayerChanged( QgsVectorLayer *layer )
141{
142 if ( mSource != QgsLayoutItemAttributeTable::AtlasFeature || layer == mCurrentAtlasLayer )
143 {
144 //nothing to do
145 return;
146 }
147
148 //atlas feature mode, atlas layer changed, so we need to reset columns
149 if ( mCurrentAtlasLayer )
150 {
151 //disconnect from previous layer
152 disconnect( mCurrentAtlasLayer, &QgsVectorLayer::layerModified, this, &QgsLayoutTable::refreshAttributes );
153 }
154
155 const bool mustRebuildColumns = static_cast< bool >( mCurrentAtlasLayer ) || mColumns.empty();
156 mCurrentAtlasLayer = layer;
157
158 if ( mustRebuildColumns )
159 {
160 //rebuild column list to match all columns from layer
161 resetColumns();
162 }
163
165
166 //listen for modifications to layer and refresh table when they occur
168}
169
171{
173 if ( !source )
174 {
175 return;
176 }
177
178 //remove existing columns
179 mColumns.clear();
180 mSortColumns.clear();
181
182 //rebuild columns list from vector layer fields
183 int idx = 0;
184 const QgsFields sourceFields = source->fields();
185
186 for ( const auto &field : sourceFields )
187 {
188 QString currentAlias = source->attributeDisplayName( idx );
190 col.setAttribute( field.name() );
191 col.setHeading( currentAlias );
192 mColumns.append( col );
193 idx++;
194 }
195}
196
197void QgsLayoutItemAttributeTable::disconnectCurrentMap()
198{
199 if ( !mMap )
200 {
201 return;
202 }
203
206 disconnect( mMap, &QObject::destroyed, this, &QgsLayoutItemAttributeTable::disconnectCurrentMap );
207 mMap = nullptr;
208}
209
211{
212 return mUseConditionalStyling;
213}
214
216{
217 if ( useConditionalStyling == mUseConditionalStyling )
218 {
219 return;
220 }
221
222 mUseConditionalStyling = useConditionalStyling;
224 emit changed();
225}
226
228{
229 if ( map == mMap )
230 {
231 //no change
232 return;
233 }
234 disconnectCurrentMap();
235
236 mMap = map;
237 if ( mMap )
238 {
239 //listen out for extent changes in linked map
242 connect( mMap, &QObject::destroyed, this, &QgsLayoutItemAttributeTable::disconnectCurrentMap );
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 mLayout->renderContext().featureFilterProvider()->filterFeatures( layer, req );
429 }
430#endif
431
432 QgsRectangle selectionRect;
433 QgsGeometry visibleRegion;
434 std::unique_ptr< QgsGeometryEngine > visibleMapEngine;
435 if ( mMap && mShowOnlyVisibleFeatures )
436 {
437 visibleRegion = QgsGeometry::fromQPolygonF( mMap->visibleExtentPolygon() );
438 selectionRect = visibleRegion.boundingBox();
439 //transform back to layer CRS
440 const QgsCoordinateTransform coordTransform( layer->crs(), mMap->crs(), mLayout->project() );
441 QgsCoordinateTransform extentTransform = coordTransform;
442 extentTransform.setBallparkTransformsAreAppropriate( true );
443 try
444 {
445 selectionRect = extentTransform.transformBoundingBox( selectionRect, Qgis::TransformDirection::Reverse );
446 visibleRegion.transform( coordTransform, Qgis::TransformDirection::Reverse );
447 }
448 catch ( QgsCsException &cse )
449 {
450 Q_UNUSED( cse )
451 return false;
452 }
453 visibleMapEngine.reset( QgsGeometry::createGeometryEngine( visibleRegion.constGet() ) );
454 visibleMapEngine->prepareGeometry();
455 }
456
457 QgsGeometry atlasGeometry;
458 std::unique_ptr< QgsGeometryEngine > atlasGeometryEngine;
459 if ( mFilterToAtlasIntersection )
460 {
461 atlasGeometry = mLayout->reportContext().currentGeometry( layer->crs() );
462 if ( !atlasGeometry.isNull() )
463 {
464 if ( selectionRect.isNull() )
465 {
466 selectionRect = atlasGeometry.boundingBox();
467 }
468 else
469 {
470 selectionRect = selectionRect.intersect( atlasGeometry.boundingBox() );
471 }
472
473 atlasGeometryEngine.reset( QgsGeometry::createGeometryEngine( atlasGeometry.constGet() ) );
474 atlasGeometryEngine->prepareGeometry();
475 }
476 }
477
479 {
480 QgsRelation relation = mLayout->project()->relationManager()->relation( mRelationId );
481 QgsFeature atlasFeature = mLayout->reportContext().feature();
482 req = relation.getRelatedFeaturesRequest( atlasFeature );
483 }
484
485 if ( !selectionRect.isEmpty() )
486 req.setFilterRect( selectionRect );
487
489
491 {
492 //source mode is current atlas feature
493 QgsFeature atlasFeature = mLayout->reportContext().feature();
494 req.setFilterFid( atlasFeature.id() );
495 }
496
497 for ( const QgsLayoutTableColumn &column : std::as_const( mSortColumns ) )
498 {
499 req.addOrderBy( column.attribute(), column.sortOrder() == Qt::AscendingOrder );
500 }
501
502 QgsFeature f;
503 int counter = 0;
504 QgsFeatureIterator fit = layer->getFeatures( req );
505
506 mConditionalStyles.clear();
507 mFeatures.clear();
508
509 QVector< QVector< Cell > > tempContents;
510 QgsLayoutTableContents existingContents;
511
512 while ( fit.nextFeature( f ) && counter < mMaximumNumberOfFeatures )
513 {
514 context.setFeature( f );
515 //check feature against filter
516 if ( activeFilter && filterExpression )
517 {
518 QVariant result = filterExpression->evaluate( &context );
519 // skip this feature if the filter evaluation is false
520 if ( !result.toBool() )
521 {
522 continue;
523 }
524 }
525
526 // check against exact map bounds
527 if ( visibleMapEngine )
528 {
529 if ( !f.hasGeometry() )
530 continue;
531
532 if ( !visibleMapEngine->intersects( f.geometry().constGet() ) )
533 continue;
534 }
535
536 //check against atlas feature intersection
537 if ( mFilterToAtlasIntersection )
538 {
539 if ( !f.hasGeometry() || !atlasGeometryEngine )
540 {
541 continue;
542 }
543
544 if ( !atlasGeometryEngine->intersects( f.geometry().constGet() ) )
545 continue;
546 }
547
548 QgsConditionalStyle rowStyle;
549
550 if ( mUseConditionalStyling )
551 {
552 const QList<QgsConditionalStyle> styles = QgsConditionalStyle::matchingConditionalStyles( conditionalStyles->rowStyles(), QVariant(), context );
553 rowStyle = QgsConditionalStyle::compressStyles( styles );
554 }
555
556 // We need to build up two different lists here -- one is a pair of the cell contents along with the cell style.
557 // 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
558 // correctly when this occurs
559 // We also need a list of just the cell contents, so that we can do a quick check for row uniqueness (when the
560 // corresponding option is enabled)
561 QVector< Cell > currentRow;
562#ifdef HAVE_SERVER_PYTHON_PLUGINS
563 mColumns = filteredColumns();
564#endif
565 currentRow.reserve( mColumns.count() );
566 QgsLayoutTableRow rowContents;
567 rowContents.reserve( mColumns.count() );
568
569 for ( const QgsLayoutTableColumn &column : std::as_const( mColumns ) )
570 {
572 int idx = layer->fields().lookupField( column.attribute() );
573 if ( idx != -1 )
574 {
575 QVariant val = f.attributes().at( idx );
576
577 if ( mUseConditionalStyling )
578 {
579 QList<QgsConditionalStyle> styles = conditionalStyles->fieldStyles( layer->fields().at( idx ).name() );
580 styles = QgsConditionalStyle::matchingConditionalStyles( styles, val, context );
581 styles.insert( 0, rowStyle );
582 style = QgsConditionalStyle::compressStyles( styles );
583 }
584
585 const QgsEditorWidgetSetup setup = layer->fields().at( idx ).editorWidgetSetup();
586
587 if ( ! setup.isNull() )
588 {
590 QVariant cache;
591
592 auto it = mLayerCache.constFind( column.attribute() );
593 if ( it != mLayerCache.constEnd() )
594 {
595 cache = it.value();
596 }
597 else
598 {
599 cache = fieldFormatter->createCache( layer, idx, setup.config() );
600 mLayerCache.insert( column.attribute(), cache );
601 }
602
603 val = fieldFormatter->representValue( layer, idx, setup.config(), cache, val );
604 }
605
606 QVariant v = QgsVariantUtils::isNull( val ) ? QString() : replaceWrapChar( val );
607 currentRow << Cell( v, style, f );
608 rowContents << v;
609 }
610 else
611 {
612 // Lets assume it's an expression
613 std::unique_ptr< QgsExpression > expression = std::make_unique< QgsExpression >( column.attribute() );
614 context.lastScope()->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "row_number" ), counter + 1, true ) );
615 expression->prepare( &context );
616 QVariant value = expression->evaluate( &context );
617
618 currentRow << Cell( value, rowStyle, f );
619 rowContents << value;
620 }
621 }
622
623 if ( mShowUniqueRowsOnly )
624 {
625 if ( contentsContainsRow( existingContents, rowContents ) )
626 continue;
627 }
628
629 tempContents << currentRow;
630 existingContents << rowContents;
631 ++counter;
632 }
633
634 // build final table contents
635 contents.reserve( tempContents.size() );
636 mConditionalStyles.reserve( tempContents.size() );
637 mFeatures.reserve( tempContents.size() );
638 for ( auto it = tempContents.constBegin(); it != tempContents.constEnd(); ++it )
639 {
641 QList< QgsConditionalStyle > rowStyles;
642 row.reserve( it->size() );
643 rowStyles.reserve( it->size() );
644
645 for ( auto cellIt = it->constBegin(); cellIt != it->constEnd(); ++cellIt )
646 {
647 row << cellIt->content;
648 rowStyles << cellIt->style;
649 if ( cellIt == it->constBegin() )
650 mFeatures << cellIt->feature;
651 }
652 contents << row;
653 mConditionalStyles << rowStyles;
654 }
655
657 return true;
658}
659
661{
662 if ( row >= mConditionalStyles.size() )
663 return QgsConditionalStyle();
664
665 return mConditionalStyles.at( row ).at( column );
666}
667
669{
671
672 const QgsConditionalStyle style = conditionalCellStyle( row, column );
673 if ( style.isValid() )
674 {
675 // apply conditional style formatting to text format
676 const QFont styleFont = style.font();
677 if ( styleFont != QFont() )
678 {
679 QFont newFont = format.font();
680 // we want to keep all the other font settings, like word/letter spacing
681 newFont.setFamily( styleFont.family() );
682
683 // warning -- there's a potential trap here! We can't just read QFont::styleName(), as that may be blank even when
684 // the font has the bold or italic attributes set! Reading the style name via QFontInfo avoids this and always returns
685 // a correct style name
686 const QString styleName = QgsFontUtils::resolveFontStyleName( styleFont );
687 if ( !styleName.isEmpty() )
688 newFont.setStyleName( styleName );
689
690 newFont.setStrikeOut( styleFont.strikeOut() );
691 newFont.setUnderline( styleFont.underline() );
692 format.setFont( newFont );
693 if ( styleName.isEmpty() )
694 {
695 // we couldn't find a direct match for the conditional font's bold/italic settings as a font style name.
696 // This means the conditional style is using Qt's "faux bold/italic" mode. Even though it causes reduced quality font
697 // rendering, we'll apply it here anyway just to ensure that the rendered font styling matches the conditional style.
698 if ( styleFont.bold() )
699 format.setForcedBold( true );
700 if ( styleFont.italic() )
701 format.setForcedItalic( true );
702 }
703 }
704 }
705
706 return format;
707}
708
710{
711 std::unique_ptr< QgsExpressionContextScope >scope( QgsLayoutTable::scopeForCell( row, column ) );
712 scope->setFeature( mFeatures.value( row ) );
713 scope->setFields( scope->feature().fields() );
714 return scope.release();
715}
716
718{
720
721 if ( mSource == LayerAttributes )
722 {
723 context.appendScope( QgsExpressionContextUtils::layerScope( mVectorLayer.get() ) );
724 }
725
726 return context;
727}
728
730{
732 if ( !mMap && !mMapUuid.isEmpty() && mLayout )
733 {
734 mMap = qobject_cast< QgsLayoutItemMap *>( mLayout->itemByUuid( mMapUuid, true ) );
735 if ( mMap )
736 {
737 //if we have found a valid map item, listen out to extent changes on it and refresh the table
740 }
741 }
742}
743
745{
747
750 {
751 mDataDefinedVectorLayer = nullptr;
752
753 QString currentLayerIdentifier;
754 if ( QgsVectorLayer *currentLayer = mVectorLayer.get() )
755 currentLayerIdentifier = currentLayer->id();
756
757 const QString layerIdentifier = mDataDefinedProperties.valueAsString( QgsLayoutObject::AttributeTableSourceLayer, context, currentLayerIdentifier );
758 QgsVectorLayer *ddLayer = qobject_cast< QgsVectorLayer * >( QgsLayoutUtils::mapLayerFromString( layerIdentifier, mLayout->project() ) );
759 if ( ddLayer )
760 mDataDefinedVectorLayer = ddLayer;
761 }
762
764}
765
766QVariant QgsLayoutItemAttributeTable::replaceWrapChar( const QVariant &variant ) const
767{
768 //avoid converting variants to string if not required (try to maintain original type for sorting)
769 if ( mWrapString.isEmpty() || !variant.toString().contains( mWrapString ) )
770 return variant;
771
772 QString replaced = variant.toString();
773 replaced.replace( mWrapString, QLatin1String( "\n" ) );
774 return replaced;
775}
776
777#ifdef HAVE_SERVER_PYTHON_PLUGINS
778QgsLayoutTableColumns QgsLayoutItemAttributeTable::filteredColumns()
779{
780
781 QgsLayoutTableColumns allowedColumns { mColumns };
782
783 // Filter columns
784 if ( mLayout->renderContext().featureFilterProvider() )
785 {
786
788
789 if ( ! source )
790 {
791 return allowedColumns;
792 }
793
794 QHash<const QString, QSet<QString>> columnAttributesMap;
795 QSet<QString> allowedAttributes;
796
797 for ( const auto &c : std::as_const( allowedColumns ) )
798 {
799 if ( ! c.attribute().isEmpty() && ! columnAttributesMap.contains( c.attribute() ) )
800 {
801 columnAttributesMap[ c.attribute() ] = QSet<QString>();
802 const QgsExpression columnExp { c.attribute() };
803 const auto constRefs { columnExp.findNodes<QgsExpressionNodeColumnRef>() };
804 for ( const auto &cref : constRefs )
805 {
806 columnAttributesMap[ c.attribute() ].insert( cref->name() );
807 allowedAttributes.insert( cref->name() );
808 }
809 }
810 }
811
812 const QStringList filteredAttributes { layout()->renderContext().featureFilterProvider()->layerAttributes( source, allowedAttributes.values() ) };
813 const QSet<QString> filteredAttributesSet( filteredAttributes.constBegin(), filteredAttributes.constEnd() );
814 if ( filteredAttributesSet != allowedAttributes )
815 {
816 const auto forbidden { allowedAttributes.subtract( filteredAttributesSet ) };
817 allowedColumns.erase( std::remove_if( allowedColumns.begin(), allowedColumns.end(), [ &columnAttributesMap, &forbidden ]( QgsLayoutTableColumn & c ) -> bool
818 {
819 for ( const auto &f : std::as_const( forbidden ) )
820 {
821 if ( columnAttributesMap[ c.attribute() ].contains( f ) )
822 {
823 return true;
824 }
825 }
826 return false;
827 } ), allowedColumns.end() );
828
829 }
830 }
831
832 return allowedColumns;
833}
834#endif
835
837{
838 switch ( mSource )
839 {
841 return mLayout->reportContext().layer();
843 {
844 if ( mDataDefinedVectorLayer )
845 return mDataDefinedVectorLayer;
846 else
847 return mVectorLayer.get();
848 }
850 {
851 QgsRelation relation = mLayout->project()->relationManager()->relation( mRelationId );
852 return relation.referencingLayer();
853 }
854 }
855 return nullptr;
856}
857
858void QgsLayoutItemAttributeTable::removeLayer( const QString &layerId )
859{
860 if ( mVectorLayer && mSource == QgsLayoutItemAttributeTable::LayerAttributes )
861 {
862 if ( layerId == mVectorLayer->id() )
863 {
864 mVectorLayer.setLayer( nullptr );
865 //remove existing columns
866 mColumns.clear();
867 }
868 }
869}
870
871void QgsLayoutItemAttributeTable::setWrapString( const QString &wrapString )
872{
873 if ( wrapString == mWrapString )
874 {
875 return;
876 }
877
878 mWrapString = wrapString;
880 emit changed();
881}
882
883bool QgsLayoutItemAttributeTable::writePropertiesToElement( QDomElement &tableElem, QDomDocument &doc, const QgsReadWriteContext &context ) const
884{
885 if ( !QgsLayoutTable::writePropertiesToElement( tableElem, doc, context ) )
886 return false;
887
888 tableElem.setAttribute( QStringLiteral( "source" ), QString::number( static_cast< int >( mSource ) ) );
889 tableElem.setAttribute( QStringLiteral( "relationId" ), mRelationId );
890 tableElem.setAttribute( QStringLiteral( "showUniqueRowsOnly" ), mShowUniqueRowsOnly );
891 tableElem.setAttribute( QStringLiteral( "showOnlyVisibleFeatures" ), mShowOnlyVisibleFeatures );
892 tableElem.setAttribute( QStringLiteral( "filterToAtlasIntersection" ), mFilterToAtlasIntersection );
893 tableElem.setAttribute( QStringLiteral( "maxFeatures" ), mMaximumNumberOfFeatures );
894 tableElem.setAttribute( QStringLiteral( "filterFeatures" ), mFilterFeatures ? QStringLiteral( "true" ) : QStringLiteral( "false" ) );
895 tableElem.setAttribute( QStringLiteral( "featureFilter" ), mFeatureFilter );
896 tableElem.setAttribute( QStringLiteral( "wrapString" ), mWrapString );
897 tableElem.setAttribute( QStringLiteral( "useConditionalStyling" ), mUseConditionalStyling );
898
899 if ( mMap )
900 {
901 tableElem.setAttribute( QStringLiteral( "mapUuid" ), mMap->uuid() );
902 }
903
904 if ( mVectorLayer )
905 {
906 tableElem.setAttribute( QStringLiteral( "vectorLayer" ), mVectorLayer.layerId );
907 tableElem.setAttribute( QStringLiteral( "vectorLayerName" ), mVectorLayer.name );
908 tableElem.setAttribute( QStringLiteral( "vectorLayerSource" ), mVectorLayer.source );
909 tableElem.setAttribute( QStringLiteral( "vectorLayerProvider" ), mVectorLayer.provider );
910 }
911 return true;
912}
913
914bool QgsLayoutItemAttributeTable::readPropertiesFromElement( const QDomElement &itemElem, const QDomDocument &doc, const QgsReadWriteContext &context )
915{
916 if ( QgsVectorLayer *prevLayer = sourceLayer() )
917 {
918 //disconnect from previous layer
920 }
921
922 if ( !QgsLayoutTable::readPropertiesFromElement( itemElem, doc, context ) )
923 return false;
924
925 mSource = QgsLayoutItemAttributeTable::ContentSource( itemElem.attribute( QStringLiteral( "source" ), QStringLiteral( "0" ) ).toInt() );
926 mRelationId = itemElem.attribute( QStringLiteral( "relationId" ), QString() );
927
929 {
930 mCurrentAtlasLayer = mLayout->reportContext().layer();
931 }
932
933 mShowUniqueRowsOnly = itemElem.attribute( QStringLiteral( "showUniqueRowsOnly" ), QStringLiteral( "0" ) ).toInt();
934 mShowOnlyVisibleFeatures = itemElem.attribute( QStringLiteral( "showOnlyVisibleFeatures" ), QStringLiteral( "1" ) ).toInt();
935 mFilterToAtlasIntersection = itemElem.attribute( QStringLiteral( "filterToAtlasIntersection" ), QStringLiteral( "0" ) ).toInt();
936 mFilterFeatures = itemElem.attribute( QStringLiteral( "filterFeatures" ), QStringLiteral( "false" ) ) == QLatin1String( "true" );
937 mFeatureFilter = itemElem.attribute( QStringLiteral( "featureFilter" ), QString() );
938 mMaximumNumberOfFeatures = itemElem.attribute( QStringLiteral( "maxFeatures" ), QStringLiteral( "5" ) ).toInt();
939 mWrapString = itemElem.attribute( QStringLiteral( "wrapString" ) );
940 mUseConditionalStyling = itemElem.attribute( QStringLiteral( "useConditionalStyling" ), QStringLiteral( "0" ) ).toInt();
941
942 //map
943 mMapUuid = itemElem.attribute( QStringLiteral( "mapUuid" ) );
944 if ( mMap )
945 {
948 mMap = nullptr;
949 }
950 // setting new mMap occurs in finalizeRestoreFromXml
951
952 //vector layer
953 QString layerId = itemElem.attribute( QStringLiteral( "vectorLayer" ) );
954 QString layerName = itemElem.attribute( QStringLiteral( "vectorLayerName" ) );
955 QString layerSource = itemElem.attribute( QStringLiteral( "vectorLayerSource" ) );
956 QString layerProvider = itemElem.attribute( QStringLiteral( "vectorLayerProvider" ) );
957 mVectorLayer = QgsVectorLayerRef( layerId, layerName, layerSource, layerProvider );
958 mVectorLayer.resolveWeakly( mLayout->project() );
959
960 //connect to new layer
961 if ( QgsVectorLayer *newLayer = sourceLayer() )
963
965
966 emit changed();
967 return true;
968}
969
971{
972 if ( source == mSource )
973 {
974 return;
975 }
976
977 QgsVectorLayer *prevLayer = sourceLayer();
978 mSource = source;
979 QgsVectorLayer *newLayer = sourceLayer();
980
981 if ( newLayer != prevLayer )
982 {
983 //disconnect from previous layer
984 if ( prevLayer )
985 {
987 }
988
989 //connect to new layer
992 {
993 mCurrentAtlasLayer = newLayer;
994 }
995
996 //layer has changed as a result of the source change, so reset column list
997 resetColumns();
998 }
999
1001 emit changed();
1002}
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:52
QString name
Definition: qgsfield.h:61
QgsEditorWidgetSetup editorWidgetSetup() const
Gets the editor widget setup for the field.
Definition: qgsfield.cpp:672
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:359
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:50
QgsLayoutRenderContext & renderContext()
Returns a reference to the layout's render context, which stores information relating to the current ...
Definition: qgslayout.cpp:365
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:105
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:501
_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.