QGIS API Documentation  3.22.4-Białowieża (ce8e65e95e)
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"
20 #include "qgslayouttablecolumn.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 "qgsgeometry.h"
29 #include "qgsexception.h"
30 #include "qgsmapsettings.h"
32 #include "qgsexpressionnodeimpl.h"
33 #include "qgsgeometryengine.h"
34 #include "qgsconditionalstyle.h"
35 
36 //
37 // QgsLayoutItemAttributeTable
38 //
39 
41  : QgsLayoutTable( layout )
42 {
43  if ( mLayout )
44  {
45  connect( mLayout->project(), static_cast < void ( QgsProject::* )( const QString & ) >( &QgsProject::layerWillBeRemoved ), this, &QgsLayoutItemAttributeTable::removeLayer );
46 
47  //coverage layer change = regenerate columns
48  connect( &mLayout->reportContext(), &QgsLayoutReportContext::layerChanged, this, &QgsLayoutItemAttributeTable::atlasLayerChanged );
49  }
51 }
52 
54 {
56 }
57 
59 {
60  return QgsApplication::getThemeIcon( QStringLiteral( "/mLayoutItemTable.svg" ) );
61 }
62 
64 {
65  return new QgsLayoutItemAttributeTable( layout );
66 }
67 
69 {
70  return tr( "<Attribute table frame>" );
71 }
72 
74 {
75  if ( layer == mVectorLayer.get() )
76  {
77  //no change
78  return;
79  }
80 
81  QgsVectorLayer *prevLayer = sourceLayer();
82  mVectorLayer.setLayer( layer );
83 
84  if ( mSource == QgsLayoutItemAttributeTable::LayerAttributes && layer != prevLayer )
85  {
86  if ( prevLayer )
87  {
88  //disconnect from previous layer
89  disconnect( prevLayer, &QgsVectorLayer::layerModified, this, &QgsLayoutTable::refreshAttributes );
90  }
91 
92  //rebuild column list to match all columns from layer
93  resetColumns();
94 
95  //listen for modifications to layer and refresh table when they occur
96  connect( mVectorLayer.get(), &QgsVectorLayer::layerModified, this, &QgsLayoutTable::refreshAttributes );
97  }
98 
100  emit changed();
101 }
102 
103 void QgsLayoutItemAttributeTable::setRelationId( const QString &relationId )
104 {
105  if ( relationId == mRelationId )
106  {
107  //no change
108  return;
109  }
110 
111  QgsVectorLayer *prevLayer = sourceLayer();
112  mRelationId = relationId;
113  QgsRelation relation = mLayout->project()->relationManager()->relation( mRelationId );
114  QgsVectorLayer *newLayer = relation.referencingLayer();
115 
116  if ( mSource == QgsLayoutItemAttributeTable::RelationChildren && newLayer != prevLayer )
117  {
118  if ( prevLayer )
119  {
120  //disconnect from previous layer
121  disconnect( prevLayer, &QgsVectorLayer::layerModified, this, &QgsLayoutTable::refreshAttributes );
122  }
123 
124  //rebuild column list to match all columns from layer
125  resetColumns();
126 
127  //listen for modifications to layer and refresh table when they occur
129  }
130 
132  emit changed();
133 }
134 
135 void QgsLayoutItemAttributeTable::atlasLayerChanged( QgsVectorLayer *layer )
136 {
137  if ( mSource != QgsLayoutItemAttributeTable::AtlasFeature || layer == mCurrentAtlasLayer )
138  {
139  //nothing to do
140  return;
141  }
142 
143  //atlas feature mode, atlas layer changed, so we need to reset columns
144  if ( mCurrentAtlasLayer )
145  {
146  //disconnect from previous layer
147  disconnect( mCurrentAtlasLayer, &QgsVectorLayer::layerModified, this, &QgsLayoutTable::refreshAttributes );
148  }
149 
150  const bool mustRebuildColumns = static_cast< bool >( mCurrentAtlasLayer ) || mColumns.empty();
151  mCurrentAtlasLayer = layer;
152 
153  if ( mustRebuildColumns )
154  {
155  //rebuild column list to match all columns from layer
156  resetColumns();
157  }
158 
160 
161  //listen for modifications to layer and refresh table when they occur
163 }
164 
166 {
168  if ( !source )
169  {
170  return;
171  }
172 
173  //remove existing columns
174  mColumns.clear();
175  mSortColumns.clear();
176 
177  //rebuild columns list from vector layer fields
178  int idx = 0;
179  const QgsFields sourceFields = source->fields();
180 
181  for ( const auto &field : sourceFields )
182  {
183  QString currentAlias = source->attributeDisplayName( idx );
185  col.setAttribute( field.name() );
186  col.setHeading( currentAlias );
187  mColumns.append( col );
188  idx++;
189  }
190 }
191 
192 void QgsLayoutItemAttributeTable::disconnectCurrentMap()
193 {
194  if ( !mMap )
195  {
196  return;
197  }
198 
201  disconnect( mMap, &QObject::destroyed, this, &QgsLayoutItemAttributeTable::disconnectCurrentMap );
202  mMap = nullptr;
203 }
204 
206 {
207  return mUseConditionalStyling;
208 }
209 
211 {
212  if ( useConditionalStyling == mUseConditionalStyling )
213  {
214  return;
215  }
216 
217  mUseConditionalStyling = useConditionalStyling;
219  emit changed();
220 }
221 
223 {
224  if ( map == mMap )
225  {
226  //no change
227  return;
228  }
229  disconnectCurrentMap();
230 
231  mMap = map;
232  if ( mMap )
233  {
234  //listen out for extent changes in linked map
237  connect( mMap, &QObject::destroyed, this, &QgsLayoutItemAttributeTable::disconnectCurrentMap );
238  }
240  emit changed();
241 }
242 
244 {
245  if ( features == mMaximumNumberOfFeatures )
246  {
247  return;
248  }
249 
250  mMaximumNumberOfFeatures = features;
252  emit changed();
253 }
254 
256 {
257  if ( uniqueOnly == mShowUniqueRowsOnly )
258  {
259  return;
260  }
261 
262  mShowUniqueRowsOnly = uniqueOnly;
264  emit changed();
265 }
266 
268 {
269  if ( visibleOnly == mShowOnlyVisibleFeatures )
270  {
271  return;
272  }
273 
274  mShowOnlyVisibleFeatures = visibleOnly;
276  emit changed();
277 }
278 
280 {
281  if ( filterToAtlas == mFilterToAtlasIntersection )
282  {
283  return;
284  }
285 
286  mFilterToAtlasIntersection = filterToAtlas;
288  emit changed();
289 }
290 
292 {
293  if ( filter == mFilterFeatures )
294  {
295  return;
296  }
297 
298  mFilterFeatures = filter;
300  emit changed();
301 }
302 
303 void QgsLayoutItemAttributeTable::setFeatureFilter( const QString &expression )
304 {
305  if ( expression == mFeatureFilter )
306  {
307  return;
308  }
309 
310  mFeatureFilter = expression;
312  emit changed();
313 }
314 
315 void QgsLayoutItemAttributeTable::setDisplayedFields( const QStringList &fields, bool refresh )
316 {
318  if ( !source )
319  {
320  return;
321  }
322 
323  //rebuild columns list, taking only fields contained in supplied list
324  mColumns.clear();
325 
326  const QgsFields layerFields = source->fields();
327 
328  if ( !fields.isEmpty() )
329  {
330  for ( const QString &field : fields )
331  {
332  int attrIdx = layerFields.lookupField( field );
333  if ( attrIdx < 0 )
334  {
335  continue;
336  }
337  QString currentAlias = source->attributeDisplayName( attrIdx );
339  col.setAttribute( layerFields.at( attrIdx ).name() );
340  col.setHeading( currentAlias );
341  mColumns.append( col );
342  }
343  }
344  else
345  {
346  //resetting, so add all attributes to columns
347  int idx = 0;
348  for ( const QgsField &field : layerFields )
349  {
350  QString currentAlias = source->attributeDisplayName( idx );
352  col.setAttribute( field.name() );
353  col.setHeading( currentAlias );
354  mColumns.append( col );
355  idx++;
356  }
357  }
358 
359  if ( refresh )
360  {
362  }
363 }
364 
365 void QgsLayoutItemAttributeTable::restoreFieldAliasMap( const QMap<int, QString> &map )
366 {
368  if ( !source )
369  {
370  return;
371  }
372 
373  for ( int i = 0; i < mColumns.count(); i++ )
374  {
375  int attrIdx = source->fields().lookupField( mColumns[i].attribute() );
376  if ( map.contains( attrIdx ) )
377  {
378  mColumns[i].setHeading( map.value( attrIdx ) );
379  }
380  else
381  {
382  mColumns[i].setHeading( source->attributeDisplayName( attrIdx ) );
383  }
384  }
385 }
386 
388 {
389  contents.clear();
390 
391  QgsVectorLayer *layer = sourceLayer();
392  if ( !layer )
393  {
394  //no source layer
395  return false;
396  }
397 
398  const QgsConditionalLayerStyles *conditionalStyles = layer->conditionalStyles();
399 
401  context.setFields( layer->fields() );
402 
403  QgsFeatureRequest req;
404  req.setExpressionContext( context );
405 
406  //prepare filter expression
407  std::unique_ptr<QgsExpression> filterExpression;
408  bool activeFilter = false;
409  if ( mFilterFeatures && !mFeatureFilter.isEmpty() )
410  {
411  filterExpression = std::make_unique< QgsExpression >( mFeatureFilter );
412  if ( !filterExpression->hasParserError() )
413  {
414  activeFilter = true;
415  req.setFilterExpression( mFeatureFilter );
416  }
417  }
418 
419 #ifdef HAVE_SERVER_PYTHON_PLUGINS
420  if ( mLayout->renderContext().featureFilterProvider() )
421  {
422  mLayout->renderContext().featureFilterProvider()->filterFeatures( layer, req );
423  }
424 #endif
425 
426  QgsRectangle selectionRect;
427  QgsGeometry visibleRegion;
428  std::unique_ptr< QgsGeometryEngine > visibleMapEngine;
429  if ( mMap && mShowOnlyVisibleFeatures )
430  {
431  visibleRegion = QgsGeometry::fromQPolygonF( mMap->visibleExtentPolygon() );
432  selectionRect = visibleRegion.boundingBox();
433  //transform back to layer CRS
434  const QgsCoordinateTransform coordTransform( layer->crs(), mMap->crs(), mLayout->project() );
435  QgsCoordinateTransform extentTransform = coordTransform;
436  extentTransform.setBallparkTransformsAreAppropriate( true );
437  try
438  {
439  selectionRect = extentTransform.transformBoundingBox( selectionRect, Qgis::TransformDirection::Reverse );
440  visibleRegion.transform( coordTransform, Qgis::TransformDirection::Reverse );
441  }
442  catch ( QgsCsException &cse )
443  {
444  Q_UNUSED( cse )
445  return false;
446  }
447  visibleMapEngine.reset( QgsGeometry::createGeometryEngine( visibleRegion.constGet() ) );
448  visibleMapEngine->prepareGeometry();
449  }
450 
451  QgsGeometry atlasGeometry;
452  std::unique_ptr< QgsGeometryEngine > atlasGeometryEngine;
453  if ( mFilterToAtlasIntersection )
454  {
455  atlasGeometry = mLayout->reportContext().currentGeometry( layer->crs() );
456  if ( !atlasGeometry.isNull() )
457  {
458  if ( selectionRect.isNull() )
459  {
460  selectionRect = atlasGeometry.boundingBox();
461  }
462  else
463  {
464  selectionRect = selectionRect.intersect( atlasGeometry.boundingBox() );
465  }
466 
467  atlasGeometryEngine.reset( QgsGeometry::createGeometryEngine( atlasGeometry.constGet() ) );
468  atlasGeometryEngine->prepareGeometry();
469  }
470  }
471 
473  {
474  QgsRelation relation = mLayout->project()->relationManager()->relation( mRelationId );
475  QgsFeature atlasFeature = mLayout->reportContext().feature();
476  req = relation.getRelatedFeaturesRequest( atlasFeature );
477  }
478 
479  if ( !selectionRect.isEmpty() )
480  req.setFilterRect( selectionRect );
481 
482  req.setFlags( mShowOnlyVisibleFeatures ? QgsFeatureRequest::ExactIntersect : QgsFeatureRequest::NoFlags );
483 
485  {
486  //source mode is current atlas feature
487  QgsFeature atlasFeature = mLayout->reportContext().feature();
488  req.setFilterFid( atlasFeature.id() );
489  }
490 
491  for ( const QgsLayoutTableColumn &column : std::as_const( mSortColumns ) )
492  {
493  req.addOrderBy( column.attribute(), column.sortOrder() == Qt::AscendingOrder );
494  }
495 
496  QgsFeature f;
497  int counter = 0;
498  QgsFeatureIterator fit = layer->getFeatures( req );
499 
500  mConditionalStyles.clear();
501  mFeatures.clear();
502 
503  QVector< QVector< Cell > > tempContents;
504  QgsLayoutTableContents existingContents;
505 
506  while ( fit.nextFeature( f ) && counter < mMaximumNumberOfFeatures )
507  {
508  context.setFeature( f );
509  //check feature against filter
510  if ( activeFilter && filterExpression )
511  {
512  QVariant result = filterExpression->evaluate( &context );
513  // skip this feature if the filter evaluation is false
514  if ( !result.toBool() )
515  {
516  continue;
517  }
518  }
519 
520  // check against exact map bounds
521  if ( visibleMapEngine )
522  {
523  if ( !f.hasGeometry() )
524  continue;
525 
526  if ( !visibleMapEngine->intersects( f.geometry().constGet() ) )
527  continue;
528  }
529 
530  //check against atlas feature intersection
531  if ( mFilterToAtlasIntersection )
532  {
533  if ( !f.hasGeometry() || !atlasGeometryEngine )
534  {
535  continue;
536  }
537 
538  if ( !atlasGeometryEngine->intersects( f.geometry().constGet() ) )
539  continue;
540  }
541 
542  QgsConditionalStyle rowStyle;
543 
544  if ( mUseConditionalStyling )
545  {
546  const QList<QgsConditionalStyle> styles = QgsConditionalStyle::matchingConditionalStyles( conditionalStyles->rowStyles(), QVariant(), context );
547  rowStyle = QgsConditionalStyle::compressStyles( styles );
548  }
549 
550  // We need to build up two different lists here -- one is a pair of the cell contents along with the cell style.
551  // 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
552  // correctly when this occurs
553  // We also need a list of just the cell contents, so that we can do a quick check for row uniqueness (when the
554  // corresponding option is enabled)
555  QVector< Cell > currentRow;
556 #ifdef HAVE_SERVER_PYTHON_PLUGINS
557  mColumns = filteredColumns();
558 #endif
559  currentRow.reserve( mColumns.count() );
560  QgsLayoutTableRow rowContents;
561  rowContents.reserve( mColumns.count() );
562 
563  for ( const QgsLayoutTableColumn &column : std::as_const( mColumns ) )
564  {
565  int idx = layer->fields().lookupField( column.attribute() );
566 
567  QgsConditionalStyle style;
568 
569  if ( idx != -1 )
570  {
571 
572  const QVariant val = f.attributes().at( idx );
573 
574  if ( mUseConditionalStyling )
575  {
576  QList<QgsConditionalStyle> styles = conditionalStyles->fieldStyles( layer->fields().at( idx ).name() );
577  styles = QgsConditionalStyle::matchingConditionalStyles( styles, val, context );
578  styles.insert( 0, rowStyle );
579  style = QgsConditionalStyle::compressStyles( styles );
580  }
581 
582  QVariant v = val.isNull() ? QString() : replaceWrapChar( val );
583  currentRow << Cell( v, style, f );
584  rowContents << v;
585  }
586  else
587  {
588  // Lets assume it's an expression
589  std::unique_ptr< QgsExpression > expression = std::make_unique< QgsExpression >( column.attribute() );
590  context.lastScope()->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "row_number" ), counter + 1, true ) );
591  expression->prepare( &context );
592  QVariant value = expression->evaluate( &context );
593 
594  currentRow << Cell( value, rowStyle, f );
595  rowContents << value;
596  }
597  }
598 
599  if ( mShowUniqueRowsOnly )
600  {
601  if ( contentsContainsRow( existingContents, rowContents ) )
602  continue;
603  }
604 
605  tempContents << currentRow;
606  existingContents << rowContents;
607  ++counter;
608  }
609 
610  // build final table contents
611  contents.reserve( tempContents.size() );
612  mConditionalStyles.reserve( tempContents.size() );
613  mFeatures.reserve( tempContents.size() );
614  for ( auto it = tempContents.constBegin(); it != tempContents.constEnd(); ++it )
615  {
616  QgsLayoutTableRow row;
617  QList< QgsConditionalStyle > rowStyles;
618  row.reserve( it->size() );
619  rowStyles.reserve( it->size() );
620 
621  for ( auto cellIt = it->constBegin(); cellIt != it->constEnd(); ++cellIt )
622  {
623  row << cellIt->content;
624  rowStyles << cellIt->style;
625  if ( cellIt == it->constBegin() )
626  mFeatures << cellIt->feature;
627  }
628  contents << row;
629  mConditionalStyles << rowStyles;
630  }
631 
633  return true;
634 }
635 
637 {
638  if ( row >= mConditionalStyles.size() )
639  return QgsConditionalStyle();
640 
641  return mConditionalStyles.at( row ).at( column );
642 }
643 
645 {
646  std::unique_ptr< QgsExpressionContextScope >scope( QgsLayoutTable::scopeForCell( row, column ) );
647  scope->setFeature( mFeatures.value( row ) );
648  scope->setFields( scope->feature().fields() );
649  return scope.release();
650 }
651 
653 {
655 
656  if ( mSource == LayerAttributes )
657  {
658  context.appendScope( QgsExpressionContextUtils::layerScope( mVectorLayer.get() ) );
659  }
660 
661  return context;
662 }
663 
665 {
667  if ( !mMap && !mMapUuid.isEmpty() && mLayout )
668  {
669  mMap = qobject_cast< QgsLayoutItemMap *>( mLayout->itemByUuid( mMapUuid, true ) );
670  if ( mMap )
671  {
672  //if we have found a valid map item, listen out to extent changes on it and refresh the table
675  }
676  }
677 }
678 
680 {
682 
685  {
686  mDataDefinedVectorLayer = nullptr;
687 
688  QString currentLayerIdentifier;
689  if ( QgsVectorLayer *currentLayer = mVectorLayer.get() )
690  currentLayerIdentifier = currentLayer->id();
691 
692  const QString layerIdentifier = mDataDefinedProperties.valueAsString( QgsLayoutObject::AttributeTableSourceLayer, context, currentLayerIdentifier );
693  QgsVectorLayer *ddLayer = qobject_cast< QgsVectorLayer * >( QgsLayoutUtils::mapLayerFromString( layerIdentifier, mLayout->project() ) );
694  if ( ddLayer )
695  mDataDefinedVectorLayer = ddLayer;
696  }
697 
699 }
700 
701 QVariant QgsLayoutItemAttributeTable::replaceWrapChar( const QVariant &variant ) const
702 {
703  //avoid converting variants to string if not required (try to maintain original type for sorting)
704  if ( mWrapString.isEmpty() || !variant.toString().contains( mWrapString ) )
705  return variant;
706 
707  QString replaced = variant.toString();
708  replaced.replace( mWrapString, QLatin1String( "\n" ) );
709  return replaced;
710 }
711 
712 #ifdef HAVE_SERVER_PYTHON_PLUGINS
713 QgsLayoutTableColumns QgsLayoutItemAttributeTable::filteredColumns()
714 {
715 
716  QgsLayoutTableColumns allowedColumns { mColumns };
717 
718  // Filter columns
719  if ( mLayout->renderContext().featureFilterProvider() )
720  {
721 
723 
724  if ( ! source )
725  {
726  return allowedColumns;
727  }
728 
729  QHash<const QString, QSet<QString>> columnAttributesMap;
730  QSet<QString> allowedAttributes;
731 
732  for ( const auto &c : std::as_const( allowedColumns ) )
733  {
734  if ( ! c.attribute().isEmpty() && ! columnAttributesMap.contains( c.attribute() ) )
735  {
736  columnAttributesMap[ c.attribute() ] = QSet<QString>();
737  const QgsExpression columnExp { c.attribute() };
738  const auto constRefs { columnExp.findNodes<QgsExpressionNodeColumnRef>() };
739  for ( const auto &cref : constRefs )
740  {
741  columnAttributesMap[ c.attribute() ].insert( cref->name() );
742  allowedAttributes.insert( cref->name() );
743  }
744  }
745  }
746 
747  const QStringList filteredAttributes { layout()->renderContext().featureFilterProvider()->layerAttributes( source, allowedAttributes.values() ) };
748 #if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0))
749  const QSet<QString> filteredAttributesSet( filteredAttributes.constBegin(), filteredAttributes.constEnd() );
750 #else
751  const QSet<QString> filteredAttributesSet { filteredAttributes.toSet() };
752 #endif
753  if ( filteredAttributesSet != allowedAttributes )
754  {
755  const auto forbidden { allowedAttributes.subtract( filteredAttributesSet ) };
756  allowedColumns.erase( std::remove_if( allowedColumns.begin(), allowedColumns.end(), [ &columnAttributesMap, &forbidden ]( QgsLayoutTableColumn & c ) -> bool
757  {
758  for ( const auto &f : std::as_const( forbidden ) )
759  {
760  if ( columnAttributesMap[ c.attribute() ].contains( f ) )
761  {
762  return true;
763  }
764  }
765  return false;
766  } ), allowedColumns.end() );
767 
768  }
769  }
770 
771  return allowedColumns;
772 }
773 #endif
774 
776 {
777  switch ( mSource )
778  {
780  return mLayout->reportContext().layer();
782  {
783  if ( mDataDefinedVectorLayer )
784  return mDataDefinedVectorLayer;
785  else
786  return mVectorLayer.get();
787  }
789  {
790  QgsRelation relation = mLayout->project()->relationManager()->relation( mRelationId );
791  return relation.referencingLayer();
792  }
793  }
794  return nullptr;
795 }
796 
797 void QgsLayoutItemAttributeTable::removeLayer( const QString &layerId )
798 {
799  if ( mVectorLayer && mSource == QgsLayoutItemAttributeTable::LayerAttributes )
800  {
801  if ( layerId == mVectorLayer->id() )
802  {
803  mVectorLayer.setLayer( nullptr );
804  //remove existing columns
805  mColumns.clear();
806  }
807  }
808 }
809 
810 void QgsLayoutItemAttributeTable::setWrapString( const QString &wrapString )
811 {
812  if ( wrapString == mWrapString )
813  {
814  return;
815  }
816 
817  mWrapString = wrapString;
819  emit changed();
820 }
821 
822 bool QgsLayoutItemAttributeTable::writePropertiesToElement( QDomElement &tableElem, QDomDocument &doc, const QgsReadWriteContext &context ) const
823 {
824  if ( !QgsLayoutTable::writePropertiesToElement( tableElem, doc, context ) )
825  return false;
826 
827  tableElem.setAttribute( QStringLiteral( "source" ), QString::number( static_cast< int >( mSource ) ) );
828  tableElem.setAttribute( QStringLiteral( "relationId" ), mRelationId );
829  tableElem.setAttribute( QStringLiteral( "showUniqueRowsOnly" ), mShowUniqueRowsOnly );
830  tableElem.setAttribute( QStringLiteral( "showOnlyVisibleFeatures" ), mShowOnlyVisibleFeatures );
831  tableElem.setAttribute( QStringLiteral( "filterToAtlasIntersection" ), mFilterToAtlasIntersection );
832  tableElem.setAttribute( QStringLiteral( "maxFeatures" ), mMaximumNumberOfFeatures );
833  tableElem.setAttribute( QStringLiteral( "filterFeatures" ), mFilterFeatures ? QStringLiteral( "true" ) : QStringLiteral( "false" ) );
834  tableElem.setAttribute( QStringLiteral( "featureFilter" ), mFeatureFilter );
835  tableElem.setAttribute( QStringLiteral( "wrapString" ), mWrapString );
836  tableElem.setAttribute( QStringLiteral( "useConditionalStyling" ), mUseConditionalStyling );
837 
838  if ( mMap )
839  {
840  tableElem.setAttribute( QStringLiteral( "mapUuid" ), mMap->uuid() );
841  }
842 
843  if ( mVectorLayer )
844  {
845  tableElem.setAttribute( QStringLiteral( "vectorLayer" ), mVectorLayer.layerId );
846  tableElem.setAttribute( QStringLiteral( "vectorLayerName" ), mVectorLayer.name );
847  tableElem.setAttribute( QStringLiteral( "vectorLayerSource" ), mVectorLayer.source );
848  tableElem.setAttribute( QStringLiteral( "vectorLayerProvider" ), mVectorLayer.provider );
849  }
850  return true;
851 }
852 
853 bool QgsLayoutItemAttributeTable::readPropertiesFromElement( const QDomElement &itemElem, const QDomDocument &doc, const QgsReadWriteContext &context )
854 {
855  if ( QgsVectorLayer *prevLayer = sourceLayer() )
856  {
857  //disconnect from previous layer
858  disconnect( prevLayer, &QgsVectorLayer::layerModified, this, &QgsLayoutTable::refreshAttributes );
859  }
860 
861  if ( !QgsLayoutTable::readPropertiesFromElement( itemElem, doc, context ) )
862  return false;
863 
864  mSource = QgsLayoutItemAttributeTable::ContentSource( itemElem.attribute( QStringLiteral( "source" ), QStringLiteral( "0" ) ).toInt() );
865  mRelationId = itemElem.attribute( QStringLiteral( "relationId" ), QString() );
866 
868  {
869  mCurrentAtlasLayer = mLayout->reportContext().layer();
870  }
871 
872  mShowUniqueRowsOnly = itemElem.attribute( QStringLiteral( "showUniqueRowsOnly" ), QStringLiteral( "0" ) ).toInt();
873  mShowOnlyVisibleFeatures = itemElem.attribute( QStringLiteral( "showOnlyVisibleFeatures" ), QStringLiteral( "1" ) ).toInt();
874  mFilterToAtlasIntersection = itemElem.attribute( QStringLiteral( "filterToAtlasIntersection" ), QStringLiteral( "0" ) ).toInt();
875  mFilterFeatures = itemElem.attribute( QStringLiteral( "filterFeatures" ), QStringLiteral( "false" ) ) == QLatin1String( "true" );
876  mFeatureFilter = itemElem.attribute( QStringLiteral( "featureFilter" ), QString() );
877  mMaximumNumberOfFeatures = itemElem.attribute( QStringLiteral( "maxFeatures" ), QStringLiteral( "5" ) ).toInt();
878  mWrapString = itemElem.attribute( QStringLiteral( "wrapString" ) );
879  mUseConditionalStyling = itemElem.attribute( QStringLiteral( "useConditionalStyling" ), QStringLiteral( "0" ) ).toInt();
880 
881  //map
882  mMapUuid = itemElem.attribute( QStringLiteral( "mapUuid" ) );
883  if ( mMap )
884  {
887  mMap = nullptr;
888  }
889  // setting new mMap occurs in finalizeRestoreFromXml
890 
891  //vector layer
892  QString layerId = itemElem.attribute( QStringLiteral( "vectorLayer" ) );
893  QString layerName = itemElem.attribute( QStringLiteral( "vectorLayerName" ) );
894  QString layerSource = itemElem.attribute( QStringLiteral( "vectorLayerSource" ) );
895  QString layerProvider = itemElem.attribute( QStringLiteral( "vectorLayerProvider" ) );
896  mVectorLayer = QgsVectorLayerRef( layerId, layerName, layerSource, layerProvider );
897  mVectorLayer.resolveWeakly( mLayout->project() );
898 
899  //connect to new layer
900  if ( QgsVectorLayer *newLayer = sourceLayer() )
902 
904 
905  emit changed();
906  return true;
907 }
908 
910 {
911  if ( source == mSource )
912  {
913  return;
914  }
915 
916  QgsVectorLayer *prevLayer = sourceLayer();
917  mSource = source;
918  QgsVectorLayer *newLayer = sourceLayer();
919 
920  if ( newLayer != prevLayer )
921  {
922  //disconnect from previous layer
923  if ( prevLayer )
924  {
925  disconnect( prevLayer, &QgsVectorLayer::layerModified, this, &QgsLayoutTable::refreshAttributes );
926  }
927 
928  //connect to new layer
931  {
932  mCurrentAtlasLayer = newLayer;
933  }
934 
935  //layer has changed as a result of the source change, so reset column list
936  resetColumns();
937  }
938 
940  emit changed();
941 }
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.
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.
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
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:223
Q_GADGET QgsFeatureId id
Definition: qgsfeature.h:64
Encapsulate a field in an attribute table or data source.
Definition: qgsfield.h:51
QString name
Definition: qgsfield.h:60
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:344
A geometry is the spatial representation of a feature.
Definition: qgsgeometry.h:125
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:127
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.
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.
QgsLayoutItemMap * map() const
Returns the layout map whose extents are controlling the features shown in the 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.
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.
QgsLayoutTableContents & contents()
Returns the current contents of the table.
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.
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.
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:101
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: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...
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.
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.
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.
void setLayer(TYPE *l)
Sets the reference to point to a specified layer.
QString layerId
Original layer ID.