QGIS API Documentation  3.24.2-Tisler (13c1a02865)
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 "qgsfieldformatter.h"
30 #include "qgsgeometry.h"
31 #include "qgsexception.h"
32 #include "qgsmapsettings.h"
34 #include "qgsexpressionnodeimpl.h"
35 #include "qgsgeometryengine.h"
36 #include "qgsconditionalstyle.h"
37 
38 //
39 // QgsLayoutItemAttributeTable
40 //
41 
43  : QgsLayoutTable( layout )
44 {
45  if ( mLayout )
46  {
47  connect( mLayout->project(), static_cast < void ( QgsProject::* )( const QString & ) >( &QgsProject::layerWillBeRemoved ), this, &QgsLayoutItemAttributeTable::removeLayer );
48 
49  //coverage layer change = regenerate columns
50  connect( &mLayout->reportContext(), &QgsLayoutReportContext::layerChanged, this, &QgsLayoutItemAttributeTable::atlasLayerChanged );
51  }
53 }
54 
56 {
58 }
59 
61 {
62  return QgsApplication::getThemeIcon( QStringLiteral( "/mLayoutItemTable.svg" ) );
63 }
64 
66 {
67  return new QgsLayoutItemAttributeTable( layout );
68 }
69 
71 {
72  return tr( "<Attribute table frame>" );
73 }
74 
76 {
77  if ( layer == mVectorLayer.get() )
78  {
79  //no change
80  return;
81  }
82 
83  QgsVectorLayer *prevLayer = sourceLayer();
84  mVectorLayer.setLayer( layer );
85 
86  if ( mSource == QgsLayoutItemAttributeTable::LayerAttributes && layer != prevLayer )
87  {
88  if ( prevLayer )
89  {
90  //disconnect from previous layer
91  disconnect( prevLayer, &QgsVectorLayer::layerModified, this, &QgsLayoutTable::refreshAttributes );
92  }
93 
94  //rebuild column list to match all columns from layer
95  resetColumns();
96 
97  //listen for modifications to layer and refresh table when they occur
98  connect( mVectorLayer.get(), &QgsVectorLayer::layerModified, this, &QgsLayoutTable::refreshAttributes );
99  }
100 
102  emit changed();
103 }
104 
105 void QgsLayoutItemAttributeTable::setRelationId( const QString &relationId )
106 {
107  if ( relationId == mRelationId )
108  {
109  //no change
110  return;
111  }
112 
113  QgsVectorLayer *prevLayer = sourceLayer();
114  mRelationId = relationId;
115  QgsRelation relation = mLayout->project()->relationManager()->relation( mRelationId );
116  QgsVectorLayer *newLayer = relation.referencingLayer();
117 
118  if ( mSource == QgsLayoutItemAttributeTable::RelationChildren && newLayer != prevLayer )
119  {
120  if ( prevLayer )
121  {
122  //disconnect from previous layer
123  disconnect( prevLayer, &QgsVectorLayer::layerModified, this, &QgsLayoutTable::refreshAttributes );
124  }
125 
126  //rebuild column list to match all columns from layer
127  resetColumns();
128 
129  //listen for modifications to layer and refresh table when they occur
131  }
132 
134  emit changed();
135 }
136 
137 void QgsLayoutItemAttributeTable::atlasLayerChanged( QgsVectorLayer *layer )
138 {
139  if ( mSource != QgsLayoutItemAttributeTable::AtlasFeature || layer == mCurrentAtlasLayer )
140  {
141  //nothing to do
142  return;
143  }
144 
145  //atlas feature mode, atlas layer changed, so we need to reset columns
146  if ( mCurrentAtlasLayer )
147  {
148  //disconnect from previous layer
149  disconnect( mCurrentAtlasLayer, &QgsVectorLayer::layerModified, this, &QgsLayoutTable::refreshAttributes );
150  }
151 
152  const bool mustRebuildColumns = static_cast< bool >( mCurrentAtlasLayer ) || mColumns.empty();
153  mCurrentAtlasLayer = layer;
154 
155  if ( mustRebuildColumns )
156  {
157  //rebuild column list to match all columns from layer
158  resetColumns();
159  }
160 
162 
163  //listen for modifications to layer and refresh table when they occur
165 }
166 
168 {
170  if ( !source )
171  {
172  return;
173  }
174 
175  //remove existing columns
176  mColumns.clear();
177  mSortColumns.clear();
178 
179  //rebuild columns list from vector layer fields
180  int idx = 0;
181  const QgsFields sourceFields = source->fields();
182 
183  for ( const auto &field : sourceFields )
184  {
185  QString currentAlias = source->attributeDisplayName( idx );
187  col.setAttribute( field.name() );
188  col.setHeading( currentAlias );
189  mColumns.append( col );
190  idx++;
191  }
192 }
193 
194 void QgsLayoutItemAttributeTable::disconnectCurrentMap()
195 {
196  if ( !mMap )
197  {
198  return;
199  }
200 
203  disconnect( mMap, &QObject::destroyed, this, &QgsLayoutItemAttributeTable::disconnectCurrentMap );
204  mMap = nullptr;
205 }
206 
208 {
209  return mUseConditionalStyling;
210 }
211 
213 {
214  if ( useConditionalStyling == mUseConditionalStyling )
215  {
216  return;
217  }
218 
219  mUseConditionalStyling = useConditionalStyling;
221  emit changed();
222 }
223 
225 {
226  if ( map == mMap )
227  {
228  //no change
229  return;
230  }
231  disconnectCurrentMap();
232 
233  mMap = map;
234  if ( mMap )
235  {
236  //listen out for extent changes in linked map
239  connect( mMap, &QObject::destroyed, this, &QgsLayoutItemAttributeTable::disconnectCurrentMap );
240  }
242  emit changed();
243 }
244 
246 {
247  if ( features == mMaximumNumberOfFeatures )
248  {
249  return;
250  }
251 
252  mMaximumNumberOfFeatures = features;
254  emit changed();
255 }
256 
258 {
259  if ( uniqueOnly == mShowUniqueRowsOnly )
260  {
261  return;
262  }
263 
264  mShowUniqueRowsOnly = uniqueOnly;
266  emit changed();
267 }
268 
270 {
271  if ( visibleOnly == mShowOnlyVisibleFeatures )
272  {
273  return;
274  }
275 
276  mShowOnlyVisibleFeatures = visibleOnly;
278  emit changed();
279 }
280 
282 {
283  if ( filterToAtlas == mFilterToAtlasIntersection )
284  {
285  return;
286  }
287 
288  mFilterToAtlasIntersection = filterToAtlas;
290  emit changed();
291 }
292 
294 {
295  if ( filter == mFilterFeatures )
296  {
297  return;
298  }
299 
300  mFilterFeatures = filter;
302  emit changed();
303 }
304 
305 void QgsLayoutItemAttributeTable::setFeatureFilter( const QString &expression )
306 {
307  if ( expression == mFeatureFilter )
308  {
309  return;
310  }
311 
312  mFeatureFilter = expression;
314  emit changed();
315 }
316 
317 void QgsLayoutItemAttributeTable::setDisplayedFields( const QStringList &fields, bool refresh )
318 {
320  if ( !source )
321  {
322  return;
323  }
324 
325  //rebuild columns list, taking only fields contained in supplied list
326  mColumns.clear();
327 
328  const QgsFields layerFields = source->fields();
329 
330  if ( !fields.isEmpty() )
331  {
332  for ( const QString &field : fields )
333  {
334  int attrIdx = layerFields.lookupField( field );
335  if ( attrIdx < 0 )
336  {
337  continue;
338  }
339  QString currentAlias = source->attributeDisplayName( attrIdx );
341  col.setAttribute( layerFields.at( attrIdx ).name() );
342  col.setHeading( currentAlias );
343  mColumns.append( col );
344  }
345  }
346  else
347  {
348  //resetting, so add all attributes to columns
349  int idx = 0;
350  for ( const QgsField &field : layerFields )
351  {
352  QString currentAlias = source->attributeDisplayName( idx );
354  col.setAttribute( field.name() );
355  col.setHeading( currentAlias );
356  mColumns.append( col );
357  idx++;
358  }
359  }
360 
361  if ( refresh )
362  {
364  }
365 }
366 
367 void QgsLayoutItemAttributeTable::restoreFieldAliasMap( const QMap<int, QString> &map )
368 {
370  if ( !source )
371  {
372  return;
373  }
374 
375  for ( int i = 0; i < mColumns.count(); i++ )
376  {
377  int attrIdx = source->fields().lookupField( mColumns[i].attribute() );
378  if ( map.contains( attrIdx ) )
379  {
380  mColumns[i].setHeading( map.value( attrIdx ) );
381  }
382  else
383  {
384  mColumns[i].setHeading( source->attributeDisplayName( attrIdx ) );
385  }
386  }
387 }
388 
390 {
391  contents.clear();
392  mLayerCache.clear();
393 
394  QgsVectorLayer *layer = sourceLayer();
395  if ( !layer )
396  {
397  //no source layer
398  return false;
399  }
400 
401  const QgsConditionalLayerStyles *conditionalStyles = layer->conditionalStyles();
402 
404  context.setFields( layer->fields() );
405 
406  QgsFeatureRequest req;
407  req.setExpressionContext( context );
408 
409  //prepare filter expression
410  std::unique_ptr<QgsExpression> filterExpression;
411  bool activeFilter = false;
412  if ( mFilterFeatures && !mFeatureFilter.isEmpty() )
413  {
414  filterExpression = std::make_unique< QgsExpression >( mFeatureFilter );
415  if ( !filterExpression->hasParserError() )
416  {
417  activeFilter = true;
418  req.setFilterExpression( mFeatureFilter );
419  }
420  }
421 
422 #ifdef HAVE_SERVER_PYTHON_PLUGINS
423  if ( mLayout->renderContext().featureFilterProvider() )
424  {
425  mLayout->renderContext().featureFilterProvider()->filterFeatures( layer, req );
426  }
427 #endif
428 
429  QgsRectangle selectionRect;
430  QgsGeometry visibleRegion;
431  std::unique_ptr< QgsGeometryEngine > visibleMapEngine;
432  if ( mMap && mShowOnlyVisibleFeatures )
433  {
434  visibleRegion = QgsGeometry::fromQPolygonF( mMap->visibleExtentPolygon() );
435  selectionRect = visibleRegion.boundingBox();
436  //transform back to layer CRS
437  const QgsCoordinateTransform coordTransform( layer->crs(), mMap->crs(), mLayout->project() );
438  QgsCoordinateTransform extentTransform = coordTransform;
439  extentTransform.setBallparkTransformsAreAppropriate( true );
440  try
441  {
442  selectionRect = extentTransform.transformBoundingBox( selectionRect, Qgis::TransformDirection::Reverse );
443  visibleRegion.transform( coordTransform, Qgis::TransformDirection::Reverse );
444  }
445  catch ( QgsCsException &cse )
446  {
447  Q_UNUSED( cse )
448  return false;
449  }
450  visibleMapEngine.reset( QgsGeometry::createGeometryEngine( visibleRegion.constGet() ) );
451  visibleMapEngine->prepareGeometry();
452  }
453 
454  QgsGeometry atlasGeometry;
455  std::unique_ptr< QgsGeometryEngine > atlasGeometryEngine;
456  if ( mFilterToAtlasIntersection )
457  {
458  atlasGeometry = mLayout->reportContext().currentGeometry( layer->crs() );
459  if ( !atlasGeometry.isNull() )
460  {
461  if ( selectionRect.isNull() )
462  {
463  selectionRect = atlasGeometry.boundingBox();
464  }
465  else
466  {
467  selectionRect = selectionRect.intersect( atlasGeometry.boundingBox() );
468  }
469 
470  atlasGeometryEngine.reset( QgsGeometry::createGeometryEngine( atlasGeometry.constGet() ) );
471  atlasGeometryEngine->prepareGeometry();
472  }
473  }
474 
476  {
477  QgsRelation relation = mLayout->project()->relationManager()->relation( mRelationId );
478  QgsFeature atlasFeature = mLayout->reportContext().feature();
479  req = relation.getRelatedFeaturesRequest( atlasFeature );
480  }
481 
482  if ( !selectionRect.isEmpty() )
483  req.setFilterRect( selectionRect );
484 
485  req.setFlags( mShowOnlyVisibleFeatures ? QgsFeatureRequest::ExactIntersect : QgsFeatureRequest::NoFlags );
486 
488  {
489  //source mode is current atlas feature
490  QgsFeature atlasFeature = mLayout->reportContext().feature();
491  req.setFilterFid( atlasFeature.id() );
492  }
493 
494  for ( const QgsLayoutTableColumn &column : std::as_const( mSortColumns ) )
495  {
496  req.addOrderBy( column.attribute(), column.sortOrder() == Qt::AscendingOrder );
497  }
498 
499  QgsFeature f;
500  int counter = 0;
501  QgsFeatureIterator fit = layer->getFeatures( req );
502 
503  mConditionalStyles.clear();
504  mFeatures.clear();
505 
506  QVector< QVector< Cell > > tempContents;
507  QgsLayoutTableContents existingContents;
508 
509  while ( fit.nextFeature( f ) && counter < mMaximumNumberOfFeatures )
510  {
511  context.setFeature( f );
512  //check feature against filter
513  if ( activeFilter && filterExpression )
514  {
515  QVariant result = filterExpression->evaluate( &context );
516  // skip this feature if the filter evaluation is false
517  if ( !result.toBool() )
518  {
519  continue;
520  }
521  }
522 
523  // check against exact map bounds
524  if ( visibleMapEngine )
525  {
526  if ( !f.hasGeometry() )
527  continue;
528 
529  if ( !visibleMapEngine->intersects( f.geometry().constGet() ) )
530  continue;
531  }
532 
533  //check against atlas feature intersection
534  if ( mFilterToAtlasIntersection )
535  {
536  if ( !f.hasGeometry() || !atlasGeometryEngine )
537  {
538  continue;
539  }
540 
541  if ( !atlasGeometryEngine->intersects( f.geometry().constGet() ) )
542  continue;
543  }
544 
545  QgsConditionalStyle rowStyle;
546 
547  if ( mUseConditionalStyling )
548  {
549  const QList<QgsConditionalStyle> styles = QgsConditionalStyle::matchingConditionalStyles( conditionalStyles->rowStyles(), QVariant(), context );
550  rowStyle = QgsConditionalStyle::compressStyles( styles );
551  }
552 
553  // We need to build up two different lists here -- one is a pair of the cell contents along with the cell style.
554  // 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
555  // correctly when this occurs
556  // We also need a list of just the cell contents, so that we can do a quick check for row uniqueness (when the
557  // corresponding option is enabled)
558  QVector< Cell > currentRow;
559 #ifdef HAVE_SERVER_PYTHON_PLUGINS
560  mColumns = filteredColumns();
561 #endif
562  currentRow.reserve( mColumns.count() );
563  QgsLayoutTableRow rowContents;
564  rowContents.reserve( mColumns.count() );
565 
566  for ( const QgsLayoutTableColumn &column : std::as_const( mColumns ) )
567  {
568  int idx = layer->fields().lookupField( column.attribute() );
569 
570  QgsConditionalStyle style;
571 
572  if ( idx != -1 )
573  {
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( mVectorLayer.get(), idx, setup.config() );
600  mLayerCache.insert( column.attribute(), cache );
601  }
602 
603  val = fieldFormatter->representValue( mVectorLayer.get(), idx, setup.config(), cache, val );
604  }
605 
606  QVariant v = val.isNull() ? 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  {
640  QgsLayoutTableRow row;
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 {
670  std::unique_ptr< QgsExpressionContextScope >scope( QgsLayoutTable::scopeForCell( row, column ) );
671  scope->setFeature( mFeatures.value( row ) );
672  scope->setFields( scope->feature().fields() );
673  return scope.release();
674 }
675 
677 {
679 
680  if ( mSource == LayerAttributes )
681  {
682  context.appendScope( QgsExpressionContextUtils::layerScope( mVectorLayer.get() ) );
683  }
684 
685  return context;
686 }
687 
689 {
691  if ( !mMap && !mMapUuid.isEmpty() && mLayout )
692  {
693  mMap = qobject_cast< QgsLayoutItemMap *>( mLayout->itemByUuid( mMapUuid, true ) );
694  if ( mMap )
695  {
696  //if we have found a valid map item, listen out to extent changes on it and refresh the table
699  }
700  }
701 }
702 
704 {
706 
709  {
710  mDataDefinedVectorLayer = nullptr;
711 
712  QString currentLayerIdentifier;
713  if ( QgsVectorLayer *currentLayer = mVectorLayer.get() )
714  currentLayerIdentifier = currentLayer->id();
715 
716  const QString layerIdentifier = mDataDefinedProperties.valueAsString( QgsLayoutObject::AttributeTableSourceLayer, context, currentLayerIdentifier );
717  QgsVectorLayer *ddLayer = qobject_cast< QgsVectorLayer * >( QgsLayoutUtils::mapLayerFromString( layerIdentifier, mLayout->project() ) );
718  if ( ddLayer )
719  mDataDefinedVectorLayer = ddLayer;
720  }
721 
723 }
724 
725 QVariant QgsLayoutItemAttributeTable::replaceWrapChar( const QVariant &variant ) const
726 {
727  //avoid converting variants to string if not required (try to maintain original type for sorting)
728  if ( mWrapString.isEmpty() || !variant.toString().contains( mWrapString ) )
729  return variant;
730 
731  QString replaced = variant.toString();
732  replaced.replace( mWrapString, QLatin1String( "\n" ) );
733  return replaced;
734 }
735 
736 #ifdef HAVE_SERVER_PYTHON_PLUGINS
737 QgsLayoutTableColumns QgsLayoutItemAttributeTable::filteredColumns()
738 {
739 
740  QgsLayoutTableColumns allowedColumns { mColumns };
741 
742  // Filter columns
743  if ( mLayout->renderContext().featureFilterProvider() )
744  {
745 
747 
748  if ( ! source )
749  {
750  return allowedColumns;
751  }
752 
753  QHash<const QString, QSet<QString>> columnAttributesMap;
754  QSet<QString> allowedAttributes;
755 
756  for ( const auto &c : std::as_const( allowedColumns ) )
757  {
758  if ( ! c.attribute().isEmpty() && ! columnAttributesMap.contains( c.attribute() ) )
759  {
760  columnAttributesMap[ c.attribute() ] = QSet<QString>();
761  const QgsExpression columnExp { c.attribute() };
762  const auto constRefs { columnExp.findNodes<QgsExpressionNodeColumnRef>() };
763  for ( const auto &cref : constRefs )
764  {
765  columnAttributesMap[ c.attribute() ].insert( cref->name() );
766  allowedAttributes.insert( cref->name() );
767  }
768  }
769  }
770 
771  const QStringList filteredAttributes { layout()->renderContext().featureFilterProvider()->layerAttributes( source, allowedAttributes.values() ) };
772 #if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0))
773  const QSet<QString> filteredAttributesSet( filteredAttributes.constBegin(), filteredAttributes.constEnd() );
774 #else
775  const QSet<QString> filteredAttributesSet { filteredAttributes.toSet() };
776 #endif
777  if ( filteredAttributesSet != allowedAttributes )
778  {
779  const auto forbidden { allowedAttributes.subtract( filteredAttributesSet ) };
780  allowedColumns.erase( std::remove_if( allowedColumns.begin(), allowedColumns.end(), [ &columnAttributesMap, &forbidden ]( QgsLayoutTableColumn & c ) -> bool
781  {
782  for ( const auto &f : std::as_const( forbidden ) )
783  {
784  if ( columnAttributesMap[ c.attribute() ].contains( f ) )
785  {
786  return true;
787  }
788  }
789  return false;
790  } ), allowedColumns.end() );
791 
792  }
793  }
794 
795  return allowedColumns;
796 }
797 #endif
798 
800 {
801  switch ( mSource )
802  {
804  return mLayout->reportContext().layer();
806  {
807  if ( mDataDefinedVectorLayer )
808  return mDataDefinedVectorLayer;
809  else
810  return mVectorLayer.get();
811  }
813  {
814  QgsRelation relation = mLayout->project()->relationManager()->relation( mRelationId );
815  return relation.referencingLayer();
816  }
817  }
818  return nullptr;
819 }
820 
821 void QgsLayoutItemAttributeTable::removeLayer( const QString &layerId )
822 {
823  if ( mVectorLayer && mSource == QgsLayoutItemAttributeTable::LayerAttributes )
824  {
825  if ( layerId == mVectorLayer->id() )
826  {
827  mVectorLayer.setLayer( nullptr );
828  //remove existing columns
829  mColumns.clear();
830  }
831  }
832 }
833 
834 void QgsLayoutItemAttributeTable::setWrapString( const QString &wrapString )
835 {
836  if ( wrapString == mWrapString )
837  {
838  return;
839  }
840 
841  mWrapString = wrapString;
843  emit changed();
844 }
845 
846 bool QgsLayoutItemAttributeTable::writePropertiesToElement( QDomElement &tableElem, QDomDocument &doc, const QgsReadWriteContext &context ) const
847 {
848  if ( !QgsLayoutTable::writePropertiesToElement( tableElem, doc, context ) )
849  return false;
850 
851  tableElem.setAttribute( QStringLiteral( "source" ), QString::number( static_cast< int >( mSource ) ) );
852  tableElem.setAttribute( QStringLiteral( "relationId" ), mRelationId );
853  tableElem.setAttribute( QStringLiteral( "showUniqueRowsOnly" ), mShowUniqueRowsOnly );
854  tableElem.setAttribute( QStringLiteral( "showOnlyVisibleFeatures" ), mShowOnlyVisibleFeatures );
855  tableElem.setAttribute( QStringLiteral( "filterToAtlasIntersection" ), mFilterToAtlasIntersection );
856  tableElem.setAttribute( QStringLiteral( "maxFeatures" ), mMaximumNumberOfFeatures );
857  tableElem.setAttribute( QStringLiteral( "filterFeatures" ), mFilterFeatures ? QStringLiteral( "true" ) : QStringLiteral( "false" ) );
858  tableElem.setAttribute( QStringLiteral( "featureFilter" ), mFeatureFilter );
859  tableElem.setAttribute( QStringLiteral( "wrapString" ), mWrapString );
860  tableElem.setAttribute( QStringLiteral( "useConditionalStyling" ), mUseConditionalStyling );
861 
862  if ( mMap )
863  {
864  tableElem.setAttribute( QStringLiteral( "mapUuid" ), mMap->uuid() );
865  }
866 
867  if ( mVectorLayer )
868  {
869  tableElem.setAttribute( QStringLiteral( "vectorLayer" ), mVectorLayer.layerId );
870  tableElem.setAttribute( QStringLiteral( "vectorLayerName" ), mVectorLayer.name );
871  tableElem.setAttribute( QStringLiteral( "vectorLayerSource" ), mVectorLayer.source );
872  tableElem.setAttribute( QStringLiteral( "vectorLayerProvider" ), mVectorLayer.provider );
873  }
874  return true;
875 }
876 
877 bool QgsLayoutItemAttributeTable::readPropertiesFromElement( const QDomElement &itemElem, const QDomDocument &doc, const QgsReadWriteContext &context )
878 {
879  if ( QgsVectorLayer *prevLayer = sourceLayer() )
880  {
881  //disconnect from previous layer
882  disconnect( prevLayer, &QgsVectorLayer::layerModified, this, &QgsLayoutTable::refreshAttributes );
883  }
884 
885  if ( !QgsLayoutTable::readPropertiesFromElement( itemElem, doc, context ) )
886  return false;
887 
888  mSource = QgsLayoutItemAttributeTable::ContentSource( itemElem.attribute( QStringLiteral( "source" ), QStringLiteral( "0" ) ).toInt() );
889  mRelationId = itemElem.attribute( QStringLiteral( "relationId" ), QString() );
890 
892  {
893  mCurrentAtlasLayer = mLayout->reportContext().layer();
894  }
895 
896  mShowUniqueRowsOnly = itemElem.attribute( QStringLiteral( "showUniqueRowsOnly" ), QStringLiteral( "0" ) ).toInt();
897  mShowOnlyVisibleFeatures = itemElem.attribute( QStringLiteral( "showOnlyVisibleFeatures" ), QStringLiteral( "1" ) ).toInt();
898  mFilterToAtlasIntersection = itemElem.attribute( QStringLiteral( "filterToAtlasIntersection" ), QStringLiteral( "0" ) ).toInt();
899  mFilterFeatures = itemElem.attribute( QStringLiteral( "filterFeatures" ), QStringLiteral( "false" ) ) == QLatin1String( "true" );
900  mFeatureFilter = itemElem.attribute( QStringLiteral( "featureFilter" ), QString() );
901  mMaximumNumberOfFeatures = itemElem.attribute( QStringLiteral( "maxFeatures" ), QStringLiteral( "5" ) ).toInt();
902  mWrapString = itemElem.attribute( QStringLiteral( "wrapString" ) );
903  mUseConditionalStyling = itemElem.attribute( QStringLiteral( "useConditionalStyling" ), QStringLiteral( "0" ) ).toInt();
904 
905  //map
906  mMapUuid = itemElem.attribute( QStringLiteral( "mapUuid" ) );
907  if ( mMap )
908  {
911  mMap = nullptr;
912  }
913  // setting new mMap occurs in finalizeRestoreFromXml
914 
915  //vector layer
916  QString layerId = itemElem.attribute( QStringLiteral( "vectorLayer" ) );
917  QString layerName = itemElem.attribute( QStringLiteral( "vectorLayerName" ) );
918  QString layerSource = itemElem.attribute( QStringLiteral( "vectorLayerSource" ) );
919  QString layerProvider = itemElem.attribute( QStringLiteral( "vectorLayerProvider" ) );
920  mVectorLayer = QgsVectorLayerRef( layerId, layerName, layerSource, layerProvider );
921  mVectorLayer.resolveWeakly( mLayout->project() );
922 
923  //connect to new layer
924  if ( QgsVectorLayer *newLayer = sourceLayer() )
926 
928 
929  emit changed();
930  return true;
931 }
932 
934 {
935  if ( source == mSource )
936  {
937  return;
938  }
939 
940  QgsVectorLayer *prevLayer = sourceLayer();
941  mSource = source;
942  QgsVectorLayer *newLayer = sourceLayer();
943 
944  if ( newLayer != prevLayer )
945  {
946  //disconnect from previous layer
947  if ( prevLayer )
948  {
949  disconnect( prevLayer, &QgsVectorLayer::layerModified, this, &QgsLayoutTable::refreshAttributes );
950  }
951 
952  //connect to new layer
955  {
956  mCurrentAtlasLayer = newLayer;
957  }
958 
959  //layer has changed as a result of the source change, so reset column list
960  resetColumns();
961  }
962 
964  emit changed();
965 }
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.
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:223
Q_GADGET QgsFeatureId id
Definition: qgsfeature.h:64
QgsFieldFormatter * fieldFormatter(const QString &id) const
Gets a field formatter by its id.
A field formatter helps to handle and display values for a field.
virtual QVariant createCache(QgsVectorLayer *layer, int fieldIndex, const QVariantMap &config) const
Create a cache for a given field.
virtual QString representValue(QgsVectorLayer *layer, int fieldIndex, const QVariantMap &config, const QVariant &cache, const QVariant &value) const
Create a pretty String representation of the value.
Encapsulate a field in an attribute table or data source.
Definition: qgsfield.h:51
QString name
Definition: qgsfield.h:60
QgsEditorWidgetSetup editorWidgetSetup() const
Gets the editor widget setup for the field.
Definition: qgsfield.cpp:602
Container of fields for a vector layer.
Definition: qgsfields.h:45
QgsField at(int i) const
Returns the field at particular index (must be in range 0..N-1).
Definition: qgsfields.cpp:163
int lookupField(const QString &fieldName) const
Looks up field's index from the field name.
Definition: qgsfields.cpp:349
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.