QGIS API Documentation  3.12.1-BucureČ™ti (121cc00ff0)
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 "qgsgeometryengine.h"
33 #include "qgsconditionalstyle.h"
34 
35 //QgsLayoutAttributeTableCompare
36 
38 
42 class CORE_EXPORT QgsLayoutAttributeTableCompare
43 {
44  public:
45 
49  QgsLayoutAttributeTableCompare() = default;
50  bool operator()( const QVector< QPair< QVariant, QgsConditionalStyle > > &m1, const QVector< QPair< QVariant, QgsConditionalStyle > > &m2 )
51  {
52  return ( mAscending ? qgsVariantLessThan( m1[mCurrentSortColumn].first, m2[mCurrentSortColumn].first )
53  : qgsVariantGreaterThan( m1[mCurrentSortColumn].first, m2[mCurrentSortColumn].first ) );
54  }
55 
59  void setSortColumn( int column ) { mCurrentSortColumn = column; }
60 
65  void setAscending( bool ascending ) { mAscending = ascending; }
66 
67  private:
68  int mCurrentSortColumn = 0;
69  bool mAscending = true;
70 };
71 
73 
74 //
75 // QgsLayoutItemAttributeTable
76 //
77 
79  : QgsLayoutTable( layout )
80 {
81  if ( mLayout )
82  {
83  connect( mLayout->project(), static_cast < void ( QgsProject::* )( const QString & ) >( &QgsProject::layerWillBeRemoved ), this, &QgsLayoutItemAttributeTable::removeLayer );
84 
85  //coverage layer change = regenerate columns
86  connect( &mLayout->reportContext(), &QgsLayoutReportContext::layerChanged, this, &QgsLayoutItemAttributeTable::atlasLayerChanged );
87  }
89 }
90 
92 {
94 }
95 
97 {
98  return QgsApplication::getThemeIcon( QStringLiteral( "/mLayoutItemTable.svg" ) );
99 }
100 
102 {
103  return new QgsLayoutItemAttributeTable( layout );
104 }
105 
107 {
108  return tr( "<Attribute table frame>" );
109 }
110 
112 {
113  if ( layer == mVectorLayer.get() )
114  {
115  //no change
116  return;
117  }
118 
119  QgsVectorLayer *prevLayer = sourceLayer();
120  mVectorLayer.setLayer( layer );
121 
122  if ( mSource == QgsLayoutItemAttributeTable::LayerAttributes && layer != prevLayer )
123  {
124  if ( prevLayer )
125  {
126  //disconnect from previous layer
127  disconnect( prevLayer, &QgsVectorLayer::layerModified, this, &QgsLayoutTable::refreshAttributes );
128  }
129 
130  //rebuild column list to match all columns from layer
131  resetColumns();
132 
133  //listen for modifications to layer and refresh table when they occur
134  connect( mVectorLayer.get(), &QgsVectorLayer::layerModified, this, &QgsLayoutTable::refreshAttributes );
135  }
136 
138  emit changed();
139 }
140 
142 {
143  if ( relationId == mRelationId )
144  {
145  //no change
146  return;
147  }
148 
149  QgsVectorLayer *prevLayer = sourceLayer();
150  mRelationId = relationId;
151  QgsRelation relation = mLayout->project()->relationManager()->relation( mRelationId );
152  QgsVectorLayer *newLayer = relation.referencingLayer();
153 
154  if ( mSource == QgsLayoutItemAttributeTable::RelationChildren && newLayer != prevLayer )
155  {
156  if ( prevLayer )
157  {
158  //disconnect from previous layer
159  disconnect( prevLayer, &QgsVectorLayer::layerModified, this, &QgsLayoutTable::refreshAttributes );
160  }
161 
162  //rebuild column list to match all columns from layer
163  resetColumns();
164 
165  //listen for modifications to layer and refresh table when they occur
167  }
168 
170  emit changed();
171 }
172 
173 void QgsLayoutItemAttributeTable::atlasLayerChanged( QgsVectorLayer *layer )
174 {
175  if ( mSource != QgsLayoutItemAttributeTable::AtlasFeature || layer == mCurrentAtlasLayer )
176  {
177  //nothing to do
178  return;
179  }
180 
181  //atlas feature mode, atlas layer changed, so we need to reset columns
182  if ( mCurrentAtlasLayer )
183  {
184  //disconnect from previous layer
185  disconnect( mCurrentAtlasLayer, &QgsVectorLayer::layerModified, this, &QgsLayoutTable::refreshAttributes );
186  }
187 
188  const bool mustRebuildColumns = static_cast< bool >( mCurrentAtlasLayer ) || mColumns.empty();
189  mCurrentAtlasLayer = layer;
190 
191  if ( mustRebuildColumns )
192  {
193  //rebuild column list to match all columns from layer
194  resetColumns();
195  }
196 
198 
199  //listen for modifications to layer and refresh table when they occur
201 }
202 
204 {
206  if ( !source )
207  {
208  return;
209  }
210 
211  //remove existing columns
212  qDeleteAll( mColumns );
213  mColumns.clear();
214 
215  //rebuild columns list from vector layer fields
216  int idx = 0;
217  const QgsFields sourceFields = source->fields();
218  for ( const auto &field : sourceFields )
219  {
220  QString currentAlias = source->attributeDisplayName( idx );
221  std::unique_ptr< QgsLayoutTableColumn > col = qgis::make_unique< QgsLayoutTableColumn >();
222  col->setAttribute( field.name() );
223  col->setHeading( currentAlias );
224  mColumns.append( col.release() );
225  idx++;
226  }
227 }
228 
229 void QgsLayoutItemAttributeTable::disconnectCurrentMap()
230 {
231  if ( !mMap )
232  {
233  return;
234  }
235 
238  disconnect( mMap, &QObject::destroyed, this, &QgsLayoutItemAttributeTable::disconnectCurrentMap );
239  mMap = nullptr;
240 }
241 
243 {
244  return mUseConditionalStyling;
245 }
246 
248 {
249  if ( useConditionalStyling == mUseConditionalStyling )
250  {
251  return;
252  }
253 
254  mUseConditionalStyling = useConditionalStyling;
256  emit changed();
257 }
258 
260 {
261  if ( map == mMap )
262  {
263  //no change
264  return;
265  }
266  disconnectCurrentMap();
267 
268  mMap = map;
269  if ( mMap )
270  {
271  //listen out for extent changes in linked map
274  connect( mMap, &QObject::destroyed, this, &QgsLayoutItemAttributeTable::disconnectCurrentMap );
275  }
277  emit changed();
278 }
279 
281 {
282  if ( features == mMaximumNumberOfFeatures )
283  {
284  return;
285  }
286 
287  mMaximumNumberOfFeatures = features;
289  emit changed();
290 }
291 
293 {
294  if ( uniqueOnly == mShowUniqueRowsOnly )
295  {
296  return;
297  }
298 
299  mShowUniqueRowsOnly = uniqueOnly;
301  emit changed();
302 }
303 
305 {
306  if ( visibleOnly == mShowOnlyVisibleFeatures )
307  {
308  return;
309  }
310 
311  mShowOnlyVisibleFeatures = visibleOnly;
313  emit changed();
314 }
315 
317 {
318  if ( filterToAtlas == mFilterToAtlasIntersection )
319  {
320  return;
321  }
322 
323  mFilterToAtlasIntersection = filterToAtlas;
325  emit changed();
326 }
327 
329 {
330  if ( filter == mFilterFeatures )
331  {
332  return;
333  }
334 
335  mFilterFeatures = filter;
337  emit changed();
338 }
339 
340 void QgsLayoutItemAttributeTable::setFeatureFilter( const QString &expression )
341 {
342  if ( expression == mFeatureFilter )
343  {
344  return;
345  }
346 
347  mFeatureFilter = expression;
349  emit changed();
350 }
351 
352 void QgsLayoutItemAttributeTable::setDisplayedFields( const QStringList &fields, bool refresh )
353 {
355  if ( !source )
356  {
357  return;
358  }
359 
360  //rebuild columns list, taking only fields contained in supplied list
361  qDeleteAll( mColumns );
362  mColumns.clear();
363 
364  const QgsFields layerFields = source->fields();
365 
366  if ( !fields.isEmpty() )
367  {
368  for ( const QString &field : fields )
369  {
370  int attrIdx = layerFields.lookupField( field );
371  if ( attrIdx < 0 )
372  continue;
373 
374  QString currentAlias = source->attributeDisplayName( attrIdx );
375  std::unique_ptr< QgsLayoutTableColumn > col = qgis::make_unique< QgsLayoutTableColumn >();
376  col->setAttribute( layerFields.at( attrIdx ).name() );
377  col->setHeading( currentAlias );
378  mColumns.append( col.release() );
379  }
380  }
381  else
382  {
383  //resetting, so add all attributes to columns
384  int idx = 0;
385  for ( const QgsField &field : layerFields )
386  {
387  QString currentAlias = source->attributeDisplayName( idx );
388  std::unique_ptr< QgsLayoutTableColumn > col = qgis::make_unique< QgsLayoutTableColumn >();
389  col->setAttribute( field.name() );
390  col->setHeading( currentAlias );
391  mColumns.append( col.release() );
392  idx++;
393  }
394  }
395 
396  if ( refresh )
397  {
399  }
400 }
401 
402 void QgsLayoutItemAttributeTable::restoreFieldAliasMap( const QMap<int, QString> &map )
403 {
405  if ( !source )
406  {
407  return;
408  }
409 
410  for ( QgsLayoutTableColumn *column : qgis::as_const( mColumns ) )
411  {
412  int attrIdx = source->fields().lookupField( column->attribute() );
413  if ( map.contains( attrIdx ) )
414  {
415  column->setHeading( map.value( attrIdx ) );
416  }
417  else
418  {
419  column->setHeading( source->attributeDisplayName( attrIdx ) );
420  }
421  }
422 }
423 
425 {
426  contents.clear();
427 
428  QgsVectorLayer *layer = sourceLayer();
429  if ( !layer )
430  {
431  //no source layer
432  return false;
433  }
434 
435  const QgsConditionalLayerStyles *conditionalStyles = layer->conditionalStyles();
436 
438  context.setFields( layer->fields() );
439 
440  QgsFeatureRequest req;
441 
442  //prepare filter expression
443  std::unique_ptr<QgsExpression> filterExpression;
444  bool activeFilter = false;
445  if ( mFilterFeatures && !mFeatureFilter.isEmpty() )
446  {
447  filterExpression = qgis::make_unique< QgsExpression >( mFeatureFilter );
448  if ( !filterExpression->hasParserError() )
449  {
450  activeFilter = true;
451  req.setFilterExpression( mFeatureFilter );
452  req.setExpressionContext( context );
453  }
454  }
455 
456  QgsRectangle selectionRect;
457  QgsGeometry visibleRegion;
458  std::unique_ptr< QgsGeometryEngine > visibleMapEngine;
459  if ( mMap && mShowOnlyVisibleFeatures )
460  {
461  visibleRegion = QgsGeometry::fromQPolygonF( mMap->visibleExtentPolygon() );
462  selectionRect = visibleRegion.boundingBox();
463  if ( layer )
464  {
465  //transform back to layer CRS
466  QgsCoordinateTransform coordTransform( layer->crs(), mMap->crs(), mLayout->project() );
467  try
468  {
469  selectionRect = coordTransform.transformBoundingBox( selectionRect, QgsCoordinateTransform::ReverseTransform );
470  visibleRegion.transform( coordTransform, QgsCoordinateTransform::ReverseTransform );
471  }
472  catch ( QgsCsException &cse )
473  {
474  Q_UNUSED( cse )
475  return false;
476  }
477  }
478  visibleMapEngine.reset( QgsGeometry::createGeometryEngine( visibleRegion.constGet() ) );
479  visibleMapEngine->prepareGeometry();
480  }
481 
482  QgsGeometry atlasGeometry;
483  std::unique_ptr< QgsGeometryEngine > atlasGeometryEngine;
484  if ( mFilterToAtlasIntersection )
485  {
486  atlasGeometry = mLayout->reportContext().currentGeometry( layer->crs() );
487  if ( !atlasGeometry.isNull() )
488  {
489  if ( selectionRect.isNull() )
490  {
491  selectionRect = atlasGeometry.boundingBox();
492  }
493  else
494  {
495  selectionRect = selectionRect.intersect( atlasGeometry.boundingBox() );
496  }
497 
498  atlasGeometryEngine.reset( QgsGeometry::createGeometryEngine( atlasGeometry.constGet() ) );
499  atlasGeometryEngine->prepareGeometry();
500  }
501  }
502 
504  {
505  QgsRelation relation = mLayout->project()->relationManager()->relation( mRelationId );
506  QgsFeature atlasFeature = mLayout->reportContext().feature();
507  req = relation.getRelatedFeaturesRequest( atlasFeature );
508  }
509 
510  if ( !selectionRect.isEmpty() )
511  req.setFilterRect( selectionRect );
512 
513  req.setFlags( mShowOnlyVisibleFeatures ? QgsFeatureRequest::ExactIntersect : QgsFeatureRequest::NoFlags );
514 
516  {
517  //source mode is current atlas feature
518  QgsFeature atlasFeature = mLayout->reportContext().feature();
519  req.setFilterFid( atlasFeature.id() );
520  }
521 
522  QgsFeature f;
523  int counter = 0;
524  QgsFeatureIterator fit = layer->getFeatures( req );
525 
526  mConditionalStyles.clear();
527 
528  QVector< QVector< QPair< QVariant, QgsConditionalStyle > > > tempContents;
529  QgsLayoutTableContents existingContents;
530 
531  while ( fit.nextFeature( f ) && counter < mMaximumNumberOfFeatures )
532  {
533  context.setFeature( f );
534  //check feature against filter
535  if ( activeFilter && filterExpression )
536  {
537  QVariant result = filterExpression->evaluate( &context );
538  // skip this feature if the filter evaluation is false
539  if ( !result.toBool() )
540  {
541  continue;
542  }
543  }
544 
545  // check against exact map bounds
546  if ( visibleMapEngine )
547  {
548  if ( !f.hasGeometry() )
549  continue;
550 
551  if ( !visibleMapEngine->intersects( f.geometry().constGet() ) )
552  continue;
553  }
554 
555  //check against atlas feature intersection
556  if ( mFilterToAtlasIntersection )
557  {
558  if ( !f.hasGeometry() || !atlasGeometryEngine )
559  {
560  continue;
561  }
562 
563  if ( !atlasGeometryEngine->intersects( f.geometry().constGet() ) )
564  continue;
565  }
566 
567  QgsConditionalStyle rowStyle;
568 
569  if ( mUseConditionalStyling )
570  {
571  const QList<QgsConditionalStyle> styles = QgsConditionalStyle::matchingConditionalStyles( conditionalStyles->rowStyles(), QVariant(), context );
572  rowStyle = QgsConditionalStyle::compressStyles( styles );
573  }
574 
575  // We need to build up two different lists here -- one is a pair of the cell contents along with the cell style.
576  // 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
577  // correctly when this occurs
578  // We also need a list of just the cell contents, so that we can do a quick check for row uniqueness (when the
579  // corresponding option is enabled)
580  QVector< QPair< QVariant, QgsConditionalStyle > > currentRow;
581  currentRow.reserve( mColumns.count() );
582  QgsLayoutTableRow rowContents;
583  rowContents.reserve( mColumns.count() );
584 
585  for ( QgsLayoutTableColumn *column : qgis::as_const( mColumns ) )
586  {
587  int idx = layer->fields().lookupField( column->attribute() );
588 
589  QgsConditionalStyle style;
590 
591  if ( idx != -1 )
592  {
593  const QVariant val = f.attributes().at( idx );
594 
595  if ( mUseConditionalStyling )
596  {
597  QList<QgsConditionalStyle> styles = conditionalStyles->fieldStyles( layer->fields().at( idx ).name() );
598  styles = QgsConditionalStyle::matchingConditionalStyles( styles, val, context );
599  styles.insert( 0, rowStyle );
600  style = QgsConditionalStyle::compressStyles( styles );
601  }
602 
603  QVariant v = replaceWrapChar( val );
604  currentRow << qMakePair( v, style );
605  rowContents << v;
606  }
607  else
608  {
609  // Lets assume it's an expression
610  std::unique_ptr< QgsExpression > expression = qgis::make_unique< QgsExpression >( column->attribute() );
611  context.lastScope()->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "row_number" ), counter + 1, true ) );
612  expression->prepare( &context );
613  QVariant value = expression->evaluate( &context );
614 
615  currentRow << qMakePair( value, rowStyle );
616  rowContents << value;
617  }
618  }
619 
620  if ( mShowUniqueRowsOnly )
621  {
622  if ( contentsContainsRow( existingContents, rowContents ) )
623  continue;
624  }
625 
626  tempContents << currentRow;
627  existingContents << rowContents;
628  ++counter;
629  }
630 
631  //sort the list, starting with the last attribute
632  QgsLayoutAttributeTableCompare c;
633  QVector< QPair<int, bool> > sortColumns = sortAttributes();
634  for ( int i = sortColumns.size() - 1; i >= 0; --i )
635  {
636  c.setSortColumn( sortColumns.at( i ).first );
637  c.setAscending( sortColumns.at( i ).second );
638  std::stable_sort( tempContents.begin(), tempContents.end(), c );
639  }
640 
641  // build final table contents
642  contents.reserve( tempContents.size() );
643  mConditionalStyles.reserve( tempContents.size() );
644  for ( auto it = tempContents.constBegin(); it != tempContents.constEnd(); ++it )
645  {
646  QgsLayoutTableRow row;
647  QList< QgsConditionalStyle > rowStyles;
648  row.reserve( it->size() );
649  rowStyles.reserve( it->size() );
650 
651  for ( auto cellIt = it->constBegin(); cellIt != it->constEnd(); ++cellIt )
652  {
653  row << cellIt->first;
654  rowStyles << cellIt->second;
655  }
656  contents << row;
657  mConditionalStyles << rowStyles;
658  }
659 
661  return true;
662 }
663 
665 {
666  if ( row >= mConditionalStyles.size() )
667  return QgsConditionalStyle();
668 
669  return mConditionalStyles.at( row ).at( column );
670 }
671 
673 {
675 
676  if ( mSource == LayerAttributes )
677  {
678  context.appendScope( QgsExpressionContextUtils::layerScope( mVectorLayer.get() ) );
679  }
680 
681  return context;
682 }
683 
685 {
687  if ( !mMap && !mMapUuid.isEmpty() && mLayout )
688  {
689  mMap = qobject_cast< QgsLayoutItemMap *>( mLayout->itemByUuid( mMapUuid, true ) );
690  if ( mMap )
691  {
692  //if we have found a valid map item, listen out to extent changes on it and refresh the table
695  }
696  }
697 }
698 
700 {
702 
705  {
706  mDataDefinedVectorLayer = nullptr;
707 
708  QString currentLayerIdentifier;
709  if ( QgsVectorLayer *currentLayer = mVectorLayer.get() )
710  currentLayerIdentifier = currentLayer->id();
711 
712  const QString layerIdentifier = mDataDefinedProperties.valueAsString( QgsLayoutObject::AttributeTableSourceLayer, context, currentLayerIdentifier );
713  QgsVectorLayer *ddLayer = qobject_cast< QgsVectorLayer * >( QgsLayoutUtils::mapLayerFromString( layerIdentifier, mLayout->project() ) );
714  if ( ddLayer )
715  mDataDefinedVectorLayer = ddLayer;
716  }
717 
719 }
720 
721 QVariant QgsLayoutItemAttributeTable::replaceWrapChar( const QVariant &variant ) const
722 {
723  //avoid converting variants to string if not required (try to maintain original type for sorting)
724  if ( mWrapString.isEmpty() || !variant.toString().contains( mWrapString ) )
725  return variant;
726 
727  QString replaced = variant.toString();
728  replaced.replace( mWrapString, QLatin1String( "\n" ) );
729  return replaced;
730 }
731 
733 {
734  switch ( mSource )
735  {
737  return mLayout->reportContext().layer();
739  {
740  if ( mDataDefinedVectorLayer )
741  return mDataDefinedVectorLayer;
742  else
743  return mVectorLayer.get();
744  }
746  {
747  QgsRelation relation = mLayout->project()->relationManager()->relation( mRelationId );
748  return relation.referencingLayer();
749  }
750  }
751  return nullptr;
752 }
753 
754 void QgsLayoutItemAttributeTable::removeLayer( const QString &layerId )
755 {
756  if ( mVectorLayer && mSource == QgsLayoutItemAttributeTable::LayerAttributes )
757  {
758  if ( layerId == mVectorLayer->id() )
759  {
760  mVectorLayer.setLayer( nullptr );
761  //remove existing columns
762  qDeleteAll( mColumns );
763  mColumns.clear();
764  }
765  }
766 }
767 
768 static bool columnsBySortRank( QPair<int, QgsLayoutTableColumn * > a, QPair<int, QgsLayoutTableColumn * > b )
769 {
770  return a.second->sortByRank() < b.second->sortByRank();
771 }
772 
773 QVector<QPair<int, bool> > QgsLayoutItemAttributeTable::sortAttributes() const
774 {
775  //generate list of all sorted columns
776  QVector< QPair<int, QgsLayoutTableColumn * > > sortedColumns;
777  int idx = 0;
778  for ( QgsLayoutTableColumn *column : mColumns )
779  {
780  if ( column->sortByRank() > 0 )
781  {
782  sortedColumns.append( qMakePair( idx, column ) );
783  }
784  idx++;
785  }
786 
787  //sort columns by rank
788  std::sort( sortedColumns.begin(), sortedColumns.end(), columnsBySortRank );
789 
790  //generate list of column index, bool for sort direction (to match 2.0 api)
791  QVector<QPair<int, bool> > attributesBySortRank;
792  attributesBySortRank.reserve( sortedColumns.size() );
793  for ( auto &column : qgis::as_const( sortedColumns ) )
794  {
795  attributesBySortRank.append( qMakePair( column.first,
796  column.second->sortOrder() == Qt::AscendingOrder ) );
797  }
798  return attributesBySortRank;
799 }
800 
802 {
803  if ( wrapString == mWrapString )
804  {
805  return;
806  }
807 
808  mWrapString = wrapString;
810  emit changed();
811 }
812 
813 bool QgsLayoutItemAttributeTable::writePropertiesToElement( QDomElement &tableElem, QDomDocument &doc, const QgsReadWriteContext &context ) const
814 {
815  if ( !QgsLayoutTable::writePropertiesToElement( tableElem, doc, context ) )
816  return false;
817 
818  tableElem.setAttribute( QStringLiteral( "source" ), QString::number( static_cast< int >( mSource ) ) );
819  tableElem.setAttribute( QStringLiteral( "relationId" ), mRelationId );
820  tableElem.setAttribute( QStringLiteral( "showUniqueRowsOnly" ), mShowUniqueRowsOnly );
821  tableElem.setAttribute( QStringLiteral( "showOnlyVisibleFeatures" ), mShowOnlyVisibleFeatures );
822  tableElem.setAttribute( QStringLiteral( "filterToAtlasIntersection" ), mFilterToAtlasIntersection );
823  tableElem.setAttribute( QStringLiteral( "maxFeatures" ), mMaximumNumberOfFeatures );
824  tableElem.setAttribute( QStringLiteral( "filterFeatures" ), mFilterFeatures ? QStringLiteral( "true" ) : QStringLiteral( "false" ) );
825  tableElem.setAttribute( QStringLiteral( "featureFilter" ), mFeatureFilter );
826  tableElem.setAttribute( QStringLiteral( "wrapString" ), mWrapString );
827  tableElem.setAttribute( QStringLiteral( "useConditionalStyling" ), mUseConditionalStyling );
828 
829  if ( mMap )
830  {
831  tableElem.setAttribute( QStringLiteral( "mapUuid" ), mMap->uuid() );
832  }
833 
834  if ( mVectorLayer )
835  {
836  tableElem.setAttribute( QStringLiteral( "vectorLayer" ), mVectorLayer.layerId );
837  tableElem.setAttribute( QStringLiteral( "vectorLayerName" ), mVectorLayer.name );
838  tableElem.setAttribute( QStringLiteral( "vectorLayerSource" ), mVectorLayer.source );
839  tableElem.setAttribute( QStringLiteral( "vectorLayerProvider" ), mVectorLayer.provider );
840  }
841  return true;
842 }
843 
844 bool QgsLayoutItemAttributeTable::readPropertiesFromElement( const QDomElement &itemElem, const QDomDocument &doc, const QgsReadWriteContext &context )
845 {
846  if ( QgsVectorLayer *prevLayer = sourceLayer() )
847  {
848  //disconnect from previous layer
849  disconnect( prevLayer, &QgsVectorLayer::layerModified, this, &QgsLayoutTable::refreshAttributes );
850  }
851 
852  if ( !QgsLayoutTable::readPropertiesFromElement( itemElem, doc, context ) )
853  return false;
854 
855  mSource = QgsLayoutItemAttributeTable::ContentSource( itemElem.attribute( QStringLiteral( "source" ), QStringLiteral( "0" ) ).toInt() );
856  mRelationId = itemElem.attribute( QStringLiteral( "relationId" ), QString() );
857 
859  {
860  mCurrentAtlasLayer = mLayout->reportContext().layer();
861  }
862 
863  mShowUniqueRowsOnly = itemElem.attribute( QStringLiteral( "showUniqueRowsOnly" ), QStringLiteral( "0" ) ).toInt();
864  mShowOnlyVisibleFeatures = itemElem.attribute( QStringLiteral( "showOnlyVisibleFeatures" ), QStringLiteral( "1" ) ).toInt();
865  mFilterToAtlasIntersection = itemElem.attribute( QStringLiteral( "filterToAtlasIntersection" ), QStringLiteral( "0" ) ).toInt();
866  mFilterFeatures = itemElem.attribute( QStringLiteral( "filterFeatures" ), QStringLiteral( "false" ) ) == QLatin1String( "true" );
867  mFeatureFilter = itemElem.attribute( QStringLiteral( "featureFilter" ), QString() );
868  mMaximumNumberOfFeatures = itemElem.attribute( QStringLiteral( "maxFeatures" ), QStringLiteral( "5" ) ).toInt();
869  mWrapString = itemElem.attribute( QStringLiteral( "wrapString" ) );
870  mUseConditionalStyling = itemElem.attribute( QStringLiteral( "useConditionalStyling" ), QStringLiteral( "0" ) ).toInt();
871 
872  //map
873  mMapUuid = itemElem.attribute( QStringLiteral( "mapUuid" ) );
874  if ( mMap )
875  {
878  mMap = nullptr;
879  }
880  // setting new mMap occurs in finalizeRestoreFromXml
881 
882  //vector layer
883  QString layerId = itemElem.attribute( QStringLiteral( "vectorLayer" ) );
884  QString layerName = itemElem.attribute( QStringLiteral( "vectorLayerName" ) );
885  QString layerSource = itemElem.attribute( QStringLiteral( "vectorLayerSource" ) );
886  QString layerProvider = itemElem.attribute( QStringLiteral( "vectorLayerProvider" ) );
887  mVectorLayer = QgsVectorLayerRef( layerId, layerName, layerSource, layerProvider );
888  mVectorLayer.resolveWeakly( mLayout->project() );
889 
890  //connect to new layer
891  if ( QgsVectorLayer *newLayer = sourceLayer() )
893 
895 
896  emit changed();
897  return true;
898 }
899 
901 {
902  if ( source == mSource )
903  {
904  return;
905  }
906 
907  QgsVectorLayer *prevLayer = sourceLayer();
908  mSource = source;
909  QgsVectorLayer *newLayer = sourceLayer();
910 
911  if ( newLayer != prevLayer )
912  {
913  //disconnect from previous layer
914  if ( prevLayer )
915  {
916  disconnect( prevLayer, &QgsVectorLayer::layerModified, this, &QgsLayoutTable::refreshAttributes );
917  }
918 
919  //connect to new layer
922  {
923  mCurrentAtlasLayer = newLayer;
924  }
925 
926  //layer has changed as a result of the source change, so reset column list
927  resetColumns();
928  }
929 
931  emit changed();
932 }
int lookupField(const QString &fieldName) const
Looks up field&#39;s index from the field name.
Definition: qgsfields.cpp:324
QString displayName() const override
Returns the multiframe display name.
QgsFeatureId id
Definition: qgsfeature.h:64
The class is used as a container of context for various read/write operations on other objects...
Wrapper for iterator of features from vector data provider or vector layer.
Single variable definition for use within a QgsExpressionContextScope.
A rectangle specified with double values.
Definition: qgsrectangle.h:41
QString relationId() const
Returns the relation id which the table displays child features from.
void setMaximumNumberOfFeatures(int features)
Sets the maximum number of features shown by the table.
QString name
Definition: qgsfield.h:59
OperationResult transform(const QgsCoordinateTransform &ct, QgsCoordinateTransform::TransformDirection direction=QgsCoordinateTransform::ForwardTransform, bool transformZ=false) SIP_THROW(QgsCsException)
Transforms this geometry as described by the coordinate transform ct.
Use exact geometry intersection (slower) instead of bounding boxes.
int type() const override
Returns unique multiframe type id.
A class to display a table in the print layout, and allow the table to span over multiple frames...
void setFeature(const QgsFeature &feature)
Convenience function for setting a feature for the context.
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 extentChanged()
Emitted when the map&#39;s extent changes.
QgsVectorLayer referencingLayer
Definition: qgsrelation.h:46
bool writePropertiesToElement(QDomElement &elem, QDomDocument &doc, const QgsReadWriteContext &context) const override
Stores multiframe state within an XML DOM element.
virtual void refreshAttributes()
Refreshes the contents shown in the table by querying for new data.
Container of fields for a vector layer.
Definition: qgsfields.h:42
A geometry is the spatial representation of a feature.
Definition: qgsgeometry.h:122
static QIcon getThemeIcon(const QString &name)
Helper to get a theme icon.
bool readPropertiesFromElement(const QDomElement &itemElem, const QDomDocument &doc, const QgsReadWriteContext &context) override
Sets multiframe state from a DOM element.
Table shows attributes from related child features.
QString wrapString() const
Returns the string used to wrap the contents of the table cells by.
The feature class encapsulates a single feature including its id, geometry and a list of field/values...
Definition: qgsfeature.h:55
bool qgsVariantGreaterThan(const QVariant &lhs, const QVariant &rhs)
Compares two QVariant values and returns whether the first is greater than the second.
Definition: qgis.cpp:189
bool hasGeometry() const
Returns true if the feature has an associated geometry.
Definition: qgsfeature.cpp:197
void addVariable(const QgsExpressionContextScope::StaticVariable &variable)
Adds a variable into the context scope.
Table shows attributes from features in a vector layer.
bool qgsVariantLessThan(const QVariant &lhs, const QVariant &rhs)
Compares two QVariant values and returns whether the first is less than the second.
Definition: qgis.cpp:121
Stores properties of a column for a QgsLayoutTable.
QgsConditionalLayerStyles * conditionalStyles() const
Returns the conditional styles that are set for this layer.
QgsField at(int i) const
Gets field at particular index (must be in range 0..N-1)
Definition: qgsfields.cpp:163
void setFilterToAtlasFeature(bool filterToAtlas)
Sets attribute table to only show features which intersect the current atlas feature.
void resetColumns()
Resets the attribute table&#39;s columns to match the vector layer&#39;s fields.
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
Attribute table source layer.
QgsFeatureRequest & setExpressionContext(const QgsExpressionContext &context)
Sets the expression context used to evaluate filter expressions.
QgsFeatureRequest & setFilterFid(QgsFeatureId fid)
Sets feature ID that should be fetched.
QgsExpressionContextScope * lastScope()
Returns the last scope added to the context.
QgsFeatureRequest & setFilterExpression(const QString &expression)
Set the filter expression.
Layout graphical items for displaying a map.
QString provider
Weak reference to layer provider.
QString layerId
Original layer ID.
QgsPropertyCollection mDataDefinedProperties
QString id() const
Returns the layer&#39;s unique ID, which is used to access this layer from QgsProject.
const QgsLayout * layout() const
Returns the layout the object is attached to.
QgsFields fields() const FINAL
Returns the list of fields of this layer.
Conditional styling for a rule.
bool isEmpty() const
Returns true if the rectangle is empty.
Definition: qgsrectangle.h:426
Expression contexts are used to encapsulate the parameters around which a QgsExpression should be eva...
static QList< QgsConditionalStyle > matchingConditionalStyles(const QList< QgsConditionalStyle > &styles, const QVariant &value, QgsExpressionContext &context)
Find and return the matching styles for the value and feature.
QgsRectangle intersect(const QgsRectangle &rect) const
Returns the intersection with the given rectangle.
Definition: qgsrectangle.h:312
QString name
Weak reference to layer name.
void setFeatureFilter(const QString &expression)
Sets the expression used for filtering features in the table.
QPointer< QgsLayout > mLayout
QgsVectorLayer * sourceLayer() const
Returns the source layer for the table, considering the table source mode.
This class wraps a request for features to a vector layer (or directly its vector data provider)...
void setFilterFeatures(bool filter)
Sets whether the feature filter is active for the attribute table.
void setDisplayOnlyVisibleFeatures(bool visibleOnly)
Sets the attribute table to only show features which are visible in a map item.
QgsFeatureRequest & setFilterRect(const QgsRectangle &rectangle)
Sets the rectangle from which features will be taken.
Encapsulates a QGIS project, including sets of map layers and their styles, layouts, annotations, canvases, etc.
Definition: qgsproject.h:91
A layout table subclass that displays attributes from a vector layer.
static QgsConditionalStyle compressStyles(const QList< QgsConditionalStyle > &styles)
Compress a list of styles into a single style.
Table shows attributes from the current atlas feature.
void layerChanged(QgsVectorLayer *layer)
Emitted when the context&#39;s layer is changed.
Encapsulate a field in an attribute table or data source.
Definition: qgsfield.h:49
QgsFeatureRequest getRelatedFeaturesRequest(const QgsFeature &feature) const
Creates a request to return all the features on the referencing (child) layer which have a foreign ke...
virtual void finalizeRestoreFromXml()
Called after all pending items have been restored from XML.
void setLayer(TYPE *l)
Sets the reference to point to a specified layer.
QString source
Weak reference to layer public source.
Base class for layouts, which can contain items such as maps, labels, scalebars, etc.
Definition: qgslayout.h:49
static QgsMapLayer * mapLayerFromString(const QString &string, QgsProject *project)
Resolves a string into a map layer from a given project.
static QgsLayoutItemAttributeTable * create(QgsLayout *layout)
Returns a new QgsLayoutItemAttributeTable for the specified parent layout.
void setFields(const QgsFields &fields)
Convenience function for setting a fields for the context.
QVector< QPair< int, bool > > sortAttributes() const
Returns the attributes used to sort the table&#39;s features.
void setVectorLayer(QgsVectorLayer *layer)
Sets the vector layer from which to display feature attributes.
QPolygonF visibleExtentPolygon() const
Returns a polygon representing the current visible map extent, considering map extents and rotation...
void mapRotationChanged(double newRotation)
Emitted when the map&#39;s rotation changes.
QgsExpressionContext createExpressionContext() const override
This method needs to be reimplemented in all classes which implement this interface and return an exp...
void setSource(ContentSource source)
Sets the source for attributes to show in table body.
QList< QgsConditionalStyle > fieldStyles(const QString &fieldName) const
Returns the conditional styles set for the field with matching fieldName.
static QgsGeometryEngine * createGeometryEngine(const QgsAbstractGeometry *geometry)
Creates and returns a new geometry engine.
const QgsAbstractGeometry * constGet() const
Returns a non-modifiable (const) reference to the underlying abstract geometry primitive.
void setDisplayedFields(const QStringList &fields, bool refresh=true)
Sets the attributes to display in the table.
void layerWillBeRemoved(const QString &layerId)
Emitted when a layer is about to be removed from the registry.
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...
QgsLayoutTableColumns mColumns
Columns to show in table.
void setUniqueRowsOnly(bool uniqueOnly)
Sets attribute table to only show unique rows.
void finalizeRestoreFromXml() override
Called after all pending items have been restored from XML.
Transform from destination to source CRS.
QgsLayoutTableContents & contents()
Returns the current contents of the table.
bool writePropertiesToElement(QDomElement &elem, QDomDocument &doc, const QgsReadWriteContext &context) const override
Stores multiframe state within an XML DOM element.
QgsCoordinateReferenceSystem crs() const
Returns coordinate reference system used for rendering the map.
QgsLayoutItemAttributeTable(QgsLayout *layout)
Constructor for QgsLayoutItemAttributeTable, attached to the specified layout.
void setWrapString(const QString &wrapString)
Sets a string to wrap the contents of the table cells by.
The QgsConditionalLayerStyles class holds conditional style information for a layer.
void appendScope(QgsExpressionContextScope *scope)
Appends a scope to the end of the context.
virtual QString uuid() const
Returns the item identification string.
_LayerRef< QgsVectorLayer > QgsVectorLayerRef
QgsRectangle boundingBox() const
Returns the bounding box of the geometry.
bool isNull() const
Test if the rectangle is null (all coordinates zero or after call to setMinimal()).
Definition: qgsrectangle.h:436
static QgsGeometry fromQPolygonF(const QPolygonF &polygon)
Construct geometry from a QPolygonF.
Class for doing transforms between two map coordinate systems.
void setRelationId(const QString &id)
Sets the relation id from which to display child features.
void refreshDataDefinedProperty(QgsLayoutObject::DataDefinedProperty property=QgsLayoutObject::AllProperties) override
Refreshes a data defined property for the multi frame by reevaluating the property&#39;s value and redraw...
QgsFeatureIterator getFeatures(const QgsFeatureRequest &request=QgsFeatureRequest()) const FINAL
Queries the layer for features specified in request.
QgsConditionalStyle conditionalCellStyle(int row, int column) const override
Returns the conditional style to use for the cell at row, column.
bool useConditionalStyling() const
Returns true if the attribute table will be rendered using the conditional styling properties of the ...
QgsGeometry geometry
Definition: qgsfeature.h:67
void setMap(QgsLayoutItemMap *map)
Sets a layout map to use to limit the extent of features shown in the attribute table.
Custom exception class for Coordinate Reference System related exceptions.
Definition: qgsexception.h:65
ContentSource
Specifies the content source for the attribute table.
ContentSource source() const
Returns the source for attributes shown in the table body.
QgsLayoutItemMap * map() const
Returns the layout map whose extents are controlling the features shown in the table.
void setUseConditionalStyling(bool enabled)
Sets whether the attribute table will be rendered using the conditional styling properties of the lin...
static QgsExpressionContextScope * layerScope(const QgsMapLayer *layer)
Creates a new scope which contains variables and functions relating to a QgsMapLayer.
bool nextFeature(QgsFeature &f)
void changed()
Emitted when the object&#39;s properties change.
Represents a vector layer which manages a vector based data sets.
TYPE * get() const
Returns a pointer to the layer, or nullptr if the reference has not yet been matched to a layer...
DataDefinedProperty
Data defined properties for different item types.
bool getTableContents(QgsLayoutTableContents &contents) override
Queries the attribute table&#39;s vector layer for attributes to show in the table.
QIcon icon() const override
Returns the item&#39;s icon.
void layerModified()
Emitted when modifications has been done on layer.
QString attributeDisplayName(int index) const
Convenience function that returns the attribute alias if defined or the field name else...
bool contentsContainsRow(const QgsLayoutTableContents &contents, const QgsLayoutTableRow &row) const
Checks whether a table contents contains a given row.
QgsAttributes attributes
Definition: qgsfeature.h:65
QVector< QgsLayoutTableRow > QgsLayoutTableContents
List of QgsLayoutTableRows, representing rows and column cell contents for a QgsLayoutTable.
QgsRectangle transformBoundingBox(const QgsRectangle &rectangle, TransformDirection direction=ForwardTransform, bool handle180Crossover=false) const SIP_THROW(QgsCsException)
Transforms a rectangle from the source CRS to the destination CRS.
QgsCoordinateReferenceSystem crs
Definition: qgsmaplayer.h:86
QgsConditionalStyles rowStyles() const
Returns a list of row styles associated with the layer.
QgsExpressionContext createExpressionContext() const override
This method needs to be reimplemented in all classes which implement this interface and return an exp...
QgsFeatureRequest & setFlags(QgsFeatureRequest::Flags flags)
Sets flags that affect how features will be fetched.
bool readPropertiesFromElement(const QDomElement &itemElem, const QDomDocument &doc, const QgsReadWriteContext &context) override
Sets multiframe state from a DOM element.
All properties for item.
QVector< QVariant > QgsLayoutTableRow
List of QVariants, representing a the contents of a single row in a QgsLayoutTable.
void recalculateTableSize()
Recalculates and updates the size of the table and all table frames.
void refresh() override
virtual void refreshDataDefinedProperty(QgsLayoutObject::DataDefinedProperty property=QgsLayoutObject::AllProperties)
Refreshes a data defined property for the multi frame by reevaluating the property&#39;s value and redraw...