QGIS API Documentation 3.99.0-Master (d270888f95f)
Loading...
Searching...
No Matches
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
20#include "qgsconditionalstyle.h"
21#include "qgsexception.h"
24#include "qgsfeatureiterator.h"
25#include "qgsfieldformatter.h"
27#include "qgsfontutils.h"
28#include "qgsgeometry.h"
29#include "qgsgeometryengine.h"
30#include "qgslayout.h"
31#include "qgslayoutframe.h"
32#include "qgslayoutitemmap.h"
36#include "qgslayoututils.h"
37#include "qgsproject.h"
38#include "qgsrelationmanager.h"
39#include "qgsvariantutils.h"
40#include "qgsvectorlayer.h"
41
42#include <QString>
43
44#include "moc_qgslayoutitemattributetable.cpp"
45
46using namespace Qt::StringLiterals;
47
48//
49// QgsLayoutItemAttributeTable
50//
51
54{
55 if ( mLayout )
56 {
57 connect( mLayout->project(), static_cast < void ( QgsProject::* )( const QString & ) >( &QgsProject::layerWillBeRemoved ), this, &QgsLayoutItemAttributeTable::removeLayer );
58
59 //coverage layer change = regenerate columns
60 connect( &mLayout->reportContext(), &QgsLayoutReportContext::layerChanged, this, &QgsLayoutItemAttributeTable::atlasLayerChanged );
61 }
63}
64
69
71{
72 return QgsApplication::getThemeIcon( u"/mLayoutItemTable.svg"_s );
73}
74
79
81{
82 return tr( "<Attribute table frame>" );
83}
84
86{
87 if ( layer == mVectorLayer.get() )
88 {
89 //no change
90 return;
91 }
92
93 QgsVectorLayer *prevLayer = sourceLayer();
94 mVectorLayer.setLayer( layer );
95
96 if ( mSource == QgsLayoutItemAttributeTable::LayerAttributes && layer != prevLayer )
97 {
98 if ( prevLayer )
99 {
100 //disconnect from previous layer
102 }
103
104 //rebuild column list to match all columns from layer
105 resetColumns();
106
107 //listen for modifications to layer and refresh table when they occur
108 connect( mVectorLayer.get(), &QgsVectorLayer::layerModified, this, &QgsLayoutTable::refreshAttributes );
109 }
110
112 emit changed();
113}
114
116{
117 if ( relationId == mRelationId )
118 {
119 //no change
120 return;
121 }
122
123 QgsVectorLayer *prevLayer = sourceLayer();
124 mRelationId = relationId;
125 QgsRelation relation = mLayout->project()->relationManager()->relation( mRelationId );
126 QgsVectorLayer *newLayer = relation.referencingLayer();
127
128 if ( mSource == QgsLayoutItemAttributeTable::RelationChildren && newLayer != prevLayer )
129 {
130 if ( prevLayer )
131 {
132 //disconnect from previous layer
134 }
135
136 //rebuild column list to match all columns from layer
137 resetColumns();
138
139 //listen for modifications to layer and refresh table when they occur
141 }
142
144 emit changed();
145}
146
147void QgsLayoutItemAttributeTable::atlasLayerChanged( QgsVectorLayer *layer )
148{
149 if ( mSource != QgsLayoutItemAttributeTable::AtlasFeature || layer == mCurrentAtlasLayer )
150 {
151 //nothing to do
152 return;
153 }
154
155 //atlas feature mode, atlas layer changed, so we need to reset columns
156 if ( mCurrentAtlasLayer )
157 {
158 //disconnect from previous layer
159 disconnect( mCurrentAtlasLayer, &QgsVectorLayer::layerModified, this, &QgsLayoutTable::refreshAttributes );
160 }
161
162 const bool mustRebuildColumns = static_cast< bool >( mCurrentAtlasLayer ) || mColumns.empty();
163 mCurrentAtlasLayer = layer;
164
165 if ( mustRebuildColumns )
166 {
167 //rebuild column list to match all columns from layer
168 resetColumns();
169 }
170
172
173 //listen for modifications to layer and refresh table when they occur
175}
176
178{
180 if ( !source )
181 {
182 return;
183 }
184
185 //remove existing columns
186 mColumns.clear();
187 mSortColumns.clear();
188
189 //rebuild columns list from vector layer fields
190 int idx = 0;
191 const QgsFields sourceFields = source->fields();
192
193 for ( const auto &field : sourceFields )
194 {
195 QString currentAlias = source->attributeDisplayName( idx );
197 col.setAttribute( field.name() );
198 col.setHeading( currentAlias );
199 mColumns.append( col );
200 idx++;
201 }
202}
203
204void QgsLayoutItemAttributeTable::disconnectCurrentMap()
205{
206 if ( !mMap )
207 {
208 return;
209 }
210
213 disconnect( mMap, &QObject::destroyed, this, &QgsLayoutItemAttributeTable::disconnectCurrentMap );
214 mMap = nullptr;
215}
216
218{
219 return mUseConditionalStyling;
220}
221
223{
224 if ( useConditionalStyling == mUseConditionalStyling )
225 {
226 return;
227 }
228
229 mUseConditionalStyling = useConditionalStyling;
231 emit changed();
232}
233
235{
236 if ( map == mMap )
237 {
238 //no change
239 return;
240 }
241 disconnectCurrentMap();
242
243 mMap = map;
244 if ( mMap )
245 {
246 //listen out for extent changes in linked map
249 }
251 emit changed();
252}
253
255{
256 if ( features == mMaximumNumberOfFeatures )
257 {
258 return;
259 }
260
261 mMaximumNumberOfFeatures = features;
263 emit changed();
264}
265
267{
268 if ( uniqueOnly == mShowUniqueRowsOnly )
269 {
270 return;
271 }
272
273 mShowUniqueRowsOnly = uniqueOnly;
275 emit changed();
276}
277
279{
280 if ( visibleOnly == mShowOnlyVisibleFeatures )
281 {
282 return;
283 }
284
285 mShowOnlyVisibleFeatures = visibleOnly;
287 emit changed();
288}
289
291{
292 if ( filterToAtlas == mFilterToAtlasIntersection )
293 {
294 return;
295 }
296
297 mFilterToAtlasIntersection = filterToAtlas;
299 emit changed();
300}
301
303{
304 if ( filter == mFilterFeatures )
305 {
306 return;
307 }
308
309 mFilterFeatures = filter;
311 emit changed();
312}
313
314void QgsLayoutItemAttributeTable::setFeatureFilter( const QString &expression )
315{
316 if ( expression == mFeatureFilter )
317 {
318 return;
319 }
320
321 mFeatureFilter = expression;
323 emit changed();
324}
325
326void QgsLayoutItemAttributeTable::setDisplayedFields( const QStringList &fields, bool refresh )
327{
329 if ( !source )
330 {
331 return;
332 }
333
334 //rebuild columns list, taking only fields contained in supplied list
335 mColumns.clear();
336
337 const QgsFields layerFields = source->fields();
338
339 if ( !fields.isEmpty() )
340 {
341 for ( const QString &field : fields )
342 {
343 int attrIdx = layerFields.lookupField( field );
344 if ( attrIdx < 0 )
345 {
346 continue;
347 }
348 QString currentAlias = source->attributeDisplayName( attrIdx );
350 col.setAttribute( layerFields.at( attrIdx ).name() );
351 col.setHeading( currentAlias );
352 mColumns.append( col );
353 }
354 }
355 else
356 {
357 //resetting, so add all attributes to columns
358 int idx = 0;
359 for ( const QgsField &field : layerFields )
360 {
361 QString currentAlias = source->attributeDisplayName( idx );
363 col.setAttribute( field.name() );
364 col.setHeading( currentAlias );
365 mColumns.append( col );
366 idx++;
367 }
368 }
369
370 if ( refresh )
371 {
373 }
374}
375
376void QgsLayoutItemAttributeTable::restoreFieldAliasMap( const QMap<int, QString> &map )
377{
379 if ( !source )
380 {
381 return;
382 }
383
384 for ( int i = 0; i < mColumns.count(); i++ )
385 {
386 int attrIdx = source->fields().lookupField( mColumns[i].attribute() );
387 if ( map.contains( attrIdx ) )
388 {
389 mColumns[i].setHeading( map.value( attrIdx ) );
390 }
391 else
392 {
393 mColumns[i].setHeading( source->attributeDisplayName( attrIdx ) );
394 }
395 }
396}
397
399{
400 contents.clear();
401 mLayerCache.clear();
402
403 QgsVectorLayer *layer = sourceLayer();
404 if ( !layer )
405 {
406 //no source layer
407 return false;
408 }
409
410 const QgsConditionalLayerStyles *conditionalStyles = layer->conditionalStyles();
411
413 context.setFields( layer->fields() );
414
416 req.setExpressionContext( context );
417
418 //prepare filter expression
419 std::unique_ptr<QgsExpression> filterExpression;
420 bool activeFilter = false;
421 if ( mFilterFeatures && !mFeatureFilter.isEmpty() )
422 {
423 filterExpression = std::make_unique< QgsExpression >( mFeatureFilter );
424 if ( !filterExpression->hasParserError() )
425 {
426 activeFilter = true;
427 req.setFilterExpression( mFeatureFilter );
428 }
429 }
430
431#ifdef HAVE_SERVER_PYTHON_PLUGINS
432 if ( mLayout->renderContext().featureFilterProvider() )
433 {
434 // NOLINTBEGIN(bugprone-branch-clone)
436 if ( mLayout->renderContext().featureFilterProvider()->isFilterThreadSafe() )
437 {
438 mLayout->renderContext().featureFilterProvider()->filterFeatures( layer->id(), req );
439 }
440 else
441 {
442 mLayout->renderContext().featureFilterProvider()->filterFeatures( layer, req );
443 }
445 // NOLINTEND(bugprone-branch-clone)
446 }
447#endif
448
449 QgsRectangle selectionRect;
450 QgsGeometry visibleRegion;
451 std::unique_ptr< QgsGeometryEngine > visibleMapEngine;
452 if ( mMap && mShowOnlyVisibleFeatures )
453 {
454 visibleRegion = QgsGeometry::fromQPolygonF( mMap->visibleExtentPolygon() );
455 selectionRect = visibleRegion.boundingBox();
456 //transform back to layer CRS
457 const QgsCoordinateTransform coordTransform( layer->crs(), mMap->crs(), mLayout->project() );
458 QgsCoordinateTransform extentTransform = coordTransform;
459 extentTransform.setBallparkTransformsAreAppropriate( true );
460 try
461 {
462 selectionRect = extentTransform.transformBoundingBox( selectionRect, Qgis::TransformDirection::Reverse );
463 visibleRegion.transform( coordTransform, Qgis::TransformDirection::Reverse );
464 }
465 catch ( QgsCsException &cse )
466 {
467 Q_UNUSED( cse )
468 return false;
469 }
470 visibleMapEngine.reset( QgsGeometry::createGeometryEngine( visibleRegion.constGet() ) );
471 visibleMapEngine->prepareGeometry();
472 }
473
474 QgsGeometry atlasGeometry;
475 std::unique_ptr< QgsGeometryEngine > atlasGeometryEngine;
476 if ( mFilterToAtlasIntersection )
477 {
478 atlasGeometry = mLayout->reportContext().currentGeometry( layer->crs() );
479 if ( !atlasGeometry.isNull() )
480 {
481 if ( selectionRect.isNull() )
482 {
483 selectionRect = atlasGeometry.boundingBox();
484 }
485 else
486 {
487 selectionRect = selectionRect.intersect( atlasGeometry.boundingBox() );
488 }
489
490 atlasGeometryEngine.reset( QgsGeometry::createGeometryEngine( atlasGeometry.constGet() ) );
491 atlasGeometryEngine->prepareGeometry();
492 }
493 else
494 {
495 return false;
496 }
497 }
498
500 {
501 QgsRelation relation = mLayout->project()->relationManager()->relation( mRelationId );
502 QgsFeature atlasFeature = mLayout->reportContext().feature();
503 req = relation.getRelatedFeaturesRequest( atlasFeature );
504 }
505
506 if ( !selectionRect.isNull() )
507 req.setFilterRect( selectionRect );
508
510
512 {
513 //source mode is current atlas feature
514 QgsFeature atlasFeature = mLayout->reportContext().feature();
515 req.setFilterFid( atlasFeature.id() );
516 }
517
518 for ( const QgsLayoutTableColumn &column : std::as_const( mSortColumns ) )
519 {
520 req.addOrderBy( column.attribute(), column.sortOrder() == Qt::AscendingOrder );
521 }
522
523 QgsFeature f;
524 int counter = 0;
525 QgsFeatureIterator fit = layer->getFeatures( req );
526
527 mConditionalStyles.clear();
528 mFeatures.clear();
529
530 QVector< QVector< Cell > > tempContents;
531 QgsLayoutTableContents existingContents;
532
533 while ( fit.nextFeature( f ) && counter < mMaximumNumberOfFeatures )
534 {
535 context.setFeature( f );
536 //check feature against filter
537 if ( activeFilter && filterExpression )
538 {
539 QVariant result = filterExpression->evaluate( &context );
540 // skip this feature if the filter evaluation is false
541 if ( !result.toBool() )
542 {
543 continue;
544 }
545 }
546
547 // check against exact map bounds
548 if ( visibleMapEngine )
549 {
550 if ( !f.hasGeometry() )
551 continue;
552
553 if ( !visibleMapEngine->intersects( f.geometry().constGet() ) )
554 continue;
555 }
556
557 //check against atlas feature intersection
558 if ( atlasGeometryEngine )
559 {
560 if ( !f.hasGeometry() )
561 {
562 continue;
563 }
564
565 if ( !atlasGeometryEngine->intersects( f.geometry().constGet() ) )
566 continue;
567 }
568
569 QgsConditionalStyle rowStyle;
570
571 if ( mUseConditionalStyling )
572 {
573 const QList<QgsConditionalStyle> styles = QgsConditionalStyle::matchingConditionalStyles( conditionalStyles->rowStyles(), QVariant(), context );
574 rowStyle = QgsConditionalStyle::compressStyles( styles );
575 }
576
577 // We need to build up two different lists here -- one is a pair of the cell contents along with the cell style.
578 // 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
579 // correctly when this occurs
580 // We also need a list of just the cell contents, so that we can do a quick check for row uniqueness (when the
581 // corresponding option is enabled)
582 QVector< Cell > currentRow;
583#ifdef HAVE_SERVER_PYTHON_PLUGINS
584 mColumns = filteredColumns();
585#endif
586 currentRow.reserve( mColumns.count() );
587 QgsLayoutTableRow rowContents;
588 rowContents.reserve( mColumns.count() );
589
590 for ( const QgsLayoutTableColumn &column : std::as_const( mColumns ) )
591 {
593 int idx = layer->fields().lookupField( column.attribute() );
594 if ( idx != -1 )
595 {
596 QVariant val = f.attributes().at( idx );
597
598 if ( mUseConditionalStyling )
599 {
600 QList<QgsConditionalStyle> styles = conditionalStyles->fieldStyles( layer->fields().at( idx ).name() );
601 styles = QgsConditionalStyle::matchingConditionalStyles( styles, val, context );
602 styles.insert( 0, rowStyle );
603 style = QgsConditionalStyle::compressStyles( styles );
604 }
605
606 const QgsEditorWidgetSetup setup = layer->fields().at( idx ).editorWidgetSetup();
607
608 if ( ! setup.isNull() )
609 {
611 QVariant cache;
612
613 auto it = mLayerCache.constFind( column.attribute() );
614 if ( it != mLayerCache.constEnd() )
615 {
616 cache = it.value();
617 }
618 else
619 {
620 cache = fieldFormatter->createCache( layer, idx, setup.config() );
621 mLayerCache.insert( column.attribute(), cache );
622 }
623
624 val = fieldFormatter->representValue( layer, idx, setup.config(), cache, val );
625 }
626
627 QVariant v = QgsVariantUtils::isNull( val ) ? QString() : replaceWrapChar( val );
628 currentRow << Cell( v, style, f );
629 rowContents << v;
630 }
631 else
632 {
633 // Lets assume it's an expression
634 auto expression = std::make_unique< QgsExpression >( column.attribute() );
635 context.lastScope()->addVariable( QgsExpressionContextScope::StaticVariable( u"row_number"_s, counter + 1, true ) );
636 expression->prepare( &context );
637 QVariant value = expression->evaluate( &context );
638
639 currentRow << Cell( value, rowStyle, f );
640 rowContents << value;
641 }
642 }
643
644 if ( mShowUniqueRowsOnly )
645 {
646 if ( contentsContainsRow( existingContents, rowContents ) )
647 continue;
648 }
649
650 tempContents << currentRow;
651 existingContents << rowContents;
652 ++counter;
653 }
654
655 // build final table contents
656 contents.reserve( tempContents.size() );
657 mConditionalStyles.reserve( tempContents.size() );
658 mFeatures.reserve( tempContents.size() );
659 for ( auto it = tempContents.constBegin(); it != tempContents.constEnd(); ++it )
660 {
662 QList< QgsConditionalStyle > rowStyles;
663 row.reserve( it->size() );
664 rowStyles.reserve( it->size() );
665
666 for ( auto cellIt = it->constBegin(); cellIt != it->constEnd(); ++cellIt )
667 {
668 row << cellIt->content;
669 rowStyles << cellIt->style;
670 if ( cellIt == it->constBegin() )
671 mFeatures << cellIt->feature;
672 }
673 contents << row;
674 mConditionalStyles << rowStyles;
675 }
676
678 return true;
679}
680
682{
683 if ( row >= mConditionalStyles.size() )
684 return QgsConditionalStyle();
685
686 return mConditionalStyles.at( row ).at( column );
687}
688
690{
692
693 const QgsConditionalStyle style = conditionalCellStyle( row, column );
694 if ( style.isValid() )
695 {
696 // apply conditional style formatting to text format
697 const QFont styleFont = style.font();
698 if ( styleFont != QFont() )
699 {
700 QFont newFont = format.font();
701 // we want to keep all the other font settings, like word/letter spacing
702 QgsFontUtils::setFontFamily( newFont, styleFont.family() );
703
704 // warning -- there's a potential trap here! We can't just read QFont::styleName(), as that may be blank even when
705 // the font has the bold or italic attributes set! Reading the style name via QFontInfo avoids this and always returns
706 // a correct style name
707 const QString styleName = QgsFontUtils::resolveFontStyleName( styleFont );
708 if ( !styleName.isEmpty() )
709 newFont.setStyleName( styleName );
710
711 newFont.setStrikeOut( styleFont.strikeOut() );
712 newFont.setUnderline( styleFont.underline() );
713 format.setFont( newFont );
714 if ( styleName.isEmpty() )
715 {
716 // we couldn't find a direct match for the conditional font's bold/italic settings as a font style name.
717 // This means the conditional style is using Qt's "faux bold/italic" mode. Even though it causes reduced quality font
718 // rendering, we'll apply it here anyway just to ensure that the rendered font styling matches the conditional style.
719 if ( styleFont.bold() )
720 format.setForcedBold( true );
721 if ( styleFont.italic() )
722 format.setForcedItalic( true );
723 }
724 }
725 }
726
727 return format;
728}
729
731{
732 std::unique_ptr< QgsExpressionContextScope >scope( QgsLayoutTable::scopeForCell( row, column ) );
733 scope->setFeature( mFeatures.value( row ) );
734 scope->setFields( scope->feature().fields() );
735 return scope.release();
736}
737
739{
741
742 if ( mSource == LayerAttributes )
743 {
744 context.appendScope( QgsExpressionContextUtils::layerScope( mVectorLayer.get() ) );
745 }
746
747 return context;
748}
749
751{
753 if ( !mMap && !mMapUuid.isEmpty() && mLayout )
754 {
755 mMap = qobject_cast< QgsLayoutItemMap *>( mLayout->itemByUuid( mMapUuid, true ) );
756 if ( mMap )
757 {
758 //if we have found a valid map item, listen out to extent changes on it and refresh the table
761 }
762 }
763}
764
766{
768
771 {
772 mDataDefinedVectorLayer = nullptr;
773
774 QString currentLayerIdentifier;
775 if ( QgsVectorLayer *currentLayer = mVectorLayer.get() )
776 currentLayerIdentifier = currentLayer->id();
777
778 const QString layerIdentifier = mDataDefinedProperties.valueAsString( QgsLayoutObject::DataDefinedProperty::AttributeTableSourceLayer, context, currentLayerIdentifier );
779 QgsVectorLayer *ddLayer = qobject_cast< QgsVectorLayer * >( QgsLayoutUtils::mapLayerFromString( layerIdentifier, mLayout->project() ) );
780 if ( ddLayer )
781 mDataDefinedVectorLayer = ddLayer;
782 }
783
785}
786
787QVariant QgsLayoutItemAttributeTable::replaceWrapChar( const QVariant &variant ) const
788{
789 //avoid converting variants to string if not required (try to maintain original type for sorting)
790 if ( mWrapString.isEmpty() || !variant.toString().contains( mWrapString ) )
791 return variant;
792
793 QString replaced = variant.toString();
794 replaced.replace( mWrapString, "\n"_L1 );
795 return replaced;
796}
797
798#ifdef HAVE_SERVER_PYTHON_PLUGINS
799QgsLayoutTableColumns QgsLayoutItemAttributeTable::filteredColumns()
800{
801
802 QgsLayoutTableColumns allowedColumns { mColumns };
803
804 // Filter columns
805 if ( mLayout->renderContext().featureFilterProvider() )
806 {
807
808 QgsVectorLayer *source { sourceLayer() };
809
810 if ( ! source )
811 {
812 return allowedColumns;
813 }
814
815 QHash<const QString, QSet<QString>> columnAttributesMap;
816 QSet<QString> allowedAttributes;
817
818 for ( const auto &c : std::as_const( allowedColumns ) )
819 {
820 if ( ! c.attribute().isEmpty() && ! columnAttributesMap.contains( c.attribute() ) )
821 {
822 columnAttributesMap[ c.attribute() ] = QSet<QString>();
823 const QgsExpression columnExp { c.attribute() };
824 const auto constRefs { columnExp.findNodes<QgsExpressionNodeColumnRef>() };
825 for ( const auto &cref : constRefs )
826 {
827 columnAttributesMap[ c.attribute() ].insert( cref->name() );
828 allowedAttributes.insert( cref->name() );
829 }
830 }
831 }
832
833 const QStringList filteredAttributes { layout()->renderContext().featureFilterProvider()->layerAttributes( source, allowedAttributes.values() ) };
834 const QSet<QString> filteredAttributesSet( filteredAttributes.constBegin(), filteredAttributes.constEnd() );
835 if ( filteredAttributesSet != allowedAttributes )
836 {
837 const auto forbidden { allowedAttributes.subtract( filteredAttributesSet ) };
838 allowedColumns.erase( std::remove_if( allowedColumns.begin(), allowedColumns.end(), [ &columnAttributesMap, &forbidden ]( QgsLayoutTableColumn & c ) -> bool
839 {
840 for ( const auto &f : std::as_const( forbidden ) )
841 {
842 if ( columnAttributesMap[ c.attribute() ].contains( f ) )
843 {
844 return true;
845 }
846 }
847 return false;
848 } ), allowedColumns.end() );
849
850 }
851 }
852
853 return allowedColumns;
854}
855#endif
856
858{
859 switch ( mSource )
860 {
862 return mLayout->reportContext().layer();
864 {
865 if ( mDataDefinedVectorLayer )
866 return mDataDefinedVectorLayer;
867 else
868 return mVectorLayer.get();
869 }
871 {
872 QgsRelation relation = mLayout->project()->relationManager()->relation( mRelationId );
873 return relation.referencingLayer();
874 }
875 }
876 return nullptr;
877}
878
879void QgsLayoutItemAttributeTable::removeLayer( const QString &layerId )
880{
881 if ( mVectorLayer && mSource == QgsLayoutItemAttributeTable::LayerAttributes )
882 {
883 if ( layerId == mVectorLayer->id() )
884 {
885 mVectorLayer.setLayer( nullptr );
886 //remove existing columns
887 mColumns.clear();
888 }
889 }
890}
891
893{
894 if ( wrapString == mWrapString )
895 {
896 return;
897 }
898
899 mWrapString = wrapString;
901 emit changed();
902}
903
904bool QgsLayoutItemAttributeTable::writePropertiesToElement( QDomElement &tableElem, QDomDocument &doc, const QgsReadWriteContext &context ) const
905{
906 if ( !QgsLayoutTable::writePropertiesToElement( tableElem, doc, context ) )
907 return false;
908
909 tableElem.setAttribute( u"source"_s, QString::number( static_cast< int >( mSource ) ) );
910 tableElem.setAttribute( u"relationId"_s, mRelationId );
911 tableElem.setAttribute( u"showUniqueRowsOnly"_s, mShowUniqueRowsOnly );
912 tableElem.setAttribute( u"showOnlyVisibleFeatures"_s, mShowOnlyVisibleFeatures );
913 tableElem.setAttribute( u"filterToAtlasIntersection"_s, mFilterToAtlasIntersection );
914 tableElem.setAttribute( u"maxFeatures"_s, mMaximumNumberOfFeatures );
915 tableElem.setAttribute( u"filterFeatures"_s, mFilterFeatures ? u"true"_s : u"false"_s );
916 tableElem.setAttribute( u"featureFilter"_s, mFeatureFilter );
917 tableElem.setAttribute( u"wrapString"_s, mWrapString );
918 tableElem.setAttribute( u"useConditionalStyling"_s, mUseConditionalStyling );
919
920 if ( mMap )
921 {
922 tableElem.setAttribute( u"mapUuid"_s, mMap->uuid() );
923 }
924
925 if ( mVectorLayer )
926 {
927 tableElem.setAttribute( u"vectorLayer"_s, mVectorLayer.layerId );
928 tableElem.setAttribute( u"vectorLayerName"_s, mVectorLayer.name );
929 tableElem.setAttribute( u"vectorLayerSource"_s, mVectorLayer.source );
930 tableElem.setAttribute( u"vectorLayerProvider"_s, mVectorLayer.provider );
931 }
932 return true;
933}
934
935bool QgsLayoutItemAttributeTable::readPropertiesFromElement( const QDomElement &itemElem, const QDomDocument &doc, const QgsReadWriteContext &context )
936{
937 if ( QgsVectorLayer *prevLayer = sourceLayer() )
938 {
939 //disconnect from previous layer
941 }
942
943 if ( !QgsLayoutTable::readPropertiesFromElement( itemElem, doc, context ) )
944 return false;
945
946 mSource = QgsLayoutItemAttributeTable::ContentSource( itemElem.attribute( u"source"_s, u"0"_s ).toInt() );
947 mRelationId = itemElem.attribute( u"relationId"_s, QString() );
948
950 {
951 mCurrentAtlasLayer = mLayout->reportContext().layer();
952 }
953
954 mShowUniqueRowsOnly = itemElem.attribute( u"showUniqueRowsOnly"_s, u"0"_s ).toInt();
955 mShowOnlyVisibleFeatures = itemElem.attribute( u"showOnlyVisibleFeatures"_s, u"1"_s ).toInt();
956 mFilterToAtlasIntersection = itemElem.attribute( u"filterToAtlasIntersection"_s, u"0"_s ).toInt();
957 mFilterFeatures = itemElem.attribute( u"filterFeatures"_s, u"false"_s ) == "true"_L1;
958 mFeatureFilter = itemElem.attribute( u"featureFilter"_s, QString() );
959 mMaximumNumberOfFeatures = itemElem.attribute( u"maxFeatures"_s, u"5"_s ).toInt();
960 mWrapString = itemElem.attribute( u"wrapString"_s );
961 mUseConditionalStyling = itemElem.attribute( u"useConditionalStyling"_s, u"0"_s ).toInt();
962
963 //map
964 mMapUuid = itemElem.attribute( u"mapUuid"_s );
965 if ( mMap )
966 {
969 mMap = nullptr;
970 }
971 // setting new mMap occurs in finalizeRestoreFromXml
972
973 //vector layer
974 QString layerId = itemElem.attribute( u"vectorLayer"_s );
975 QString layerName = itemElem.attribute( u"vectorLayerName"_s );
976 QString layerSource = itemElem.attribute( u"vectorLayerSource"_s );
977 QString layerProvider = itemElem.attribute( u"vectorLayerProvider"_s );
978 mVectorLayer = QgsVectorLayerRef( layerId, layerName, layerSource, layerProvider );
979 mVectorLayer.resolveWeakly( mLayout->project() );
980
981 //connect to new layer
982 if ( QgsVectorLayer *newLayer = sourceLayer() )
984
986
987 emit changed();
988 return true;
989}
990
992{
993 if ( source == mSource )
994 {
995 return;
996 }
997
998 QgsVectorLayer *prevLayer = sourceLayer();
999 mSource = source;
1000 QgsVectorLayer *newLayer = sourceLayer();
1001
1002 if ( newLayer != prevLayer )
1003 {
1004 //disconnect from previous layer
1005 if ( prevLayer )
1006 {
1007 disconnect( prevLayer, &QgsVectorLayer::layerModified, this, &QgsLayoutTable::refreshAttributes );
1008 }
1009
1010 //connect to new layer
1013 {
1014 mCurrentAtlasLayer = newLayer;
1015 }
1016
1017 //layer has changed as a result of the source change, so reset column list
1018 resetColumns();
1019 }
1020
1022 emit changed();
1023}
@ ExactIntersect
Use exact geometry intersection (slower) instead of bounding boxes.
Definition qgis.h:2256
@ NoFlags
No flags are set.
Definition qgis.h:2253
@ Reverse
Reverse/inverse transform (from destination to source).
Definition qgis.h:2731
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.
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:60
QgsAttributes attributes
Definition qgsfeature.h:69
QgsFeatureId id
Definition qgsfeature.h:68
QgsGeometry geometry
Definition qgsfeature.h:71
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:56
QString name
Definition qgsfield.h:65
QgsEditorWidgetSetup editorWidgetSetup() const
Gets the editor widget setup for the field.
Definition qgsfield.cpp:751
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...
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.
QgsLayoutTable(QgsLayout *layout)
Constructor for QgsLayoutTable, belonging to the specified layout.
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.
QgsLayoutRenderContext & renderContext()
Returns a reference to the layout's render context, which stores information relating to the current ...
QgsCoordinateReferenceSystem crs
Definition qgsmaplayer.h:90
QString id
Definition qgsmaplayer.h:86
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:112
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:42
QgsVectorLayer * referencingLayer
Definition qgsrelation.h:47
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:7451
#define Q_NOWARN_DEPRECATED_PUSH
Definition qgis.h:7450
_LayerRef< QgsVectorLayer > QgsVectorLayerRef
Single variable definition for use within a QgsExpressionContextScope.
void setLayer(TYPE *l)
Sets the reference to point to a specified layer.