QGIS API Documentation  3.27.0-Master (aef1b1ec20)
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 #include "qgsfontutils.h"
38 
39 //
40 // QgsLayoutItemAttributeTable
41 //
42 
44  : QgsLayoutTable( layout )
45 {
46  if ( mLayout )
47  {
48  connect( mLayout->project(), static_cast < void ( QgsProject::* )( const QString & ) >( &QgsProject::layerWillBeRemoved ), this, &QgsLayoutItemAttributeTable::removeLayer );
49 
50  //coverage layer change = regenerate columns
51  connect( &mLayout->reportContext(), &QgsLayoutReportContext::layerChanged, this, &QgsLayoutItemAttributeTable::atlasLayerChanged );
52  }
54 }
55 
57 {
59 }
60 
62 {
63  return QgsApplication::getThemeIcon( QStringLiteral( "/mLayoutItemTable.svg" ) );
64 }
65 
67 {
68  return new QgsLayoutItemAttributeTable( layout );
69 }
70 
72 {
73  return tr( "<Attribute table frame>" );
74 }
75 
77 {
78  if ( layer == mVectorLayer.get() )
79  {
80  //no change
81  return;
82  }
83 
84  QgsVectorLayer *prevLayer = sourceLayer();
85  mVectorLayer.setLayer( layer );
86 
87  if ( mSource == QgsLayoutItemAttributeTable::LayerAttributes && layer != prevLayer )
88  {
89  if ( prevLayer )
90  {
91  //disconnect from previous layer
92  disconnect( prevLayer, &QgsVectorLayer::layerModified, this, &QgsLayoutTable::refreshAttributes );
93  }
94 
95  //rebuild column list to match all columns from layer
96  resetColumns();
97 
98  //listen for modifications to layer and refresh table when they occur
99  connect( mVectorLayer.get(), &QgsVectorLayer::layerModified, this, &QgsLayoutTable::refreshAttributes );
100  }
101 
103  emit changed();
104 }
105 
106 void QgsLayoutItemAttributeTable::setRelationId( const QString &relationId )
107 {
108  if ( relationId == mRelationId )
109  {
110  //no change
111  return;
112  }
113 
114  QgsVectorLayer *prevLayer = sourceLayer();
115  mRelationId = relationId;
116  QgsRelation relation = mLayout->project()->relationManager()->relation( mRelationId );
117  QgsVectorLayer *newLayer = relation.referencingLayer();
118 
119  if ( mSource == QgsLayoutItemAttributeTable::RelationChildren && newLayer != prevLayer )
120  {
121  if ( prevLayer )
122  {
123  //disconnect from previous layer
124  disconnect( prevLayer, &QgsVectorLayer::layerModified, this, &QgsLayoutTable::refreshAttributes );
125  }
126 
127  //rebuild column list to match all columns from layer
128  resetColumns();
129 
130  //listen for modifications to layer and refresh table when they occur
132  }
133 
135  emit changed();
136 }
137 
138 void QgsLayoutItemAttributeTable::atlasLayerChanged( QgsVectorLayer *layer )
139 {
140  if ( mSource != QgsLayoutItemAttributeTable::AtlasFeature || layer == mCurrentAtlasLayer )
141  {
142  //nothing to do
143  return;
144  }
145 
146  //atlas feature mode, atlas layer changed, so we need to reset columns
147  if ( mCurrentAtlasLayer )
148  {
149  //disconnect from previous layer
150  disconnect( mCurrentAtlasLayer, &QgsVectorLayer::layerModified, this, &QgsLayoutTable::refreshAttributes );
151  }
152 
153  const bool mustRebuildColumns = static_cast< bool >( mCurrentAtlasLayer ) || mColumns.empty();
154  mCurrentAtlasLayer = layer;
155 
156  if ( mustRebuildColumns )
157  {
158  //rebuild column list to match all columns from layer
159  resetColumns();
160  }
161 
163 
164  //listen for modifications to layer and refresh table when they occur
166 }
167 
169 {
171  if ( !source )
172  {
173  return;
174  }
175 
176  //remove existing columns
177  mColumns.clear();
178  mSortColumns.clear();
179 
180  //rebuild columns list from vector layer fields
181  int idx = 0;
182  const QgsFields sourceFields = source->fields();
183 
184  for ( const auto &field : sourceFields )
185  {
186  QString currentAlias = source->attributeDisplayName( idx );
188  col.setAttribute( field.name() );
189  col.setHeading( currentAlias );
190  mColumns.append( col );
191  idx++;
192  }
193 }
194 
195 void QgsLayoutItemAttributeTable::disconnectCurrentMap()
196 {
197  if ( !mMap )
198  {
199  return;
200  }
201 
204  disconnect( mMap, &QObject::destroyed, this, &QgsLayoutItemAttributeTable::disconnectCurrentMap );
205  mMap = nullptr;
206 }
207 
209 {
210  return mUseConditionalStyling;
211 }
212 
214 {
215  if ( useConditionalStyling == mUseConditionalStyling )
216  {
217  return;
218  }
219 
220  mUseConditionalStyling = useConditionalStyling;
222  emit changed();
223 }
224 
226 {
227  if ( map == mMap )
228  {
229  //no change
230  return;
231  }
232  disconnectCurrentMap();
233 
234  mMap = map;
235  if ( mMap )
236  {
237  //listen out for extent changes in linked map
240  connect( mMap, &QObject::destroyed, this, &QgsLayoutItemAttributeTable::disconnectCurrentMap );
241  }
243  emit changed();
244 }
245 
247 {
248  if ( features == mMaximumNumberOfFeatures )
249  {
250  return;
251  }
252 
253  mMaximumNumberOfFeatures = features;
255  emit changed();
256 }
257 
259 {
260  if ( uniqueOnly == mShowUniqueRowsOnly )
261  {
262  return;
263  }
264 
265  mShowUniqueRowsOnly = uniqueOnly;
267  emit changed();
268 }
269 
271 {
272  if ( visibleOnly == mShowOnlyVisibleFeatures )
273  {
274  return;
275  }
276 
277  mShowOnlyVisibleFeatures = visibleOnly;
279  emit changed();
280 }
281 
283 {
284  if ( filterToAtlas == mFilterToAtlasIntersection )
285  {
286  return;
287  }
288 
289  mFilterToAtlasIntersection = filterToAtlas;
291  emit changed();
292 }
293 
295 {
296  if ( filter == mFilterFeatures )
297  {
298  return;
299  }
300 
301  mFilterFeatures = filter;
303  emit changed();
304 }
305 
306 void QgsLayoutItemAttributeTable::setFeatureFilter( const QString &expression )
307 {
308  if ( expression == mFeatureFilter )
309  {
310  return;
311  }
312 
313  mFeatureFilter = expression;
315  emit changed();
316 }
317 
318 void QgsLayoutItemAttributeTable::setDisplayedFields( const QStringList &fields, bool refresh )
319 {
321  if ( !source )
322  {
323  return;
324  }
325 
326  //rebuild columns list, taking only fields contained in supplied list
327  mColumns.clear();
328 
329  const QgsFields layerFields = source->fields();
330 
331  if ( !fields.isEmpty() )
332  {
333  for ( const QString &field : fields )
334  {
335  int attrIdx = layerFields.lookupField( field );
336  if ( attrIdx < 0 )
337  {
338  continue;
339  }
340  QString currentAlias = source->attributeDisplayName( attrIdx );
342  col.setAttribute( layerFields.at( attrIdx ).name() );
343  col.setHeading( currentAlias );
344  mColumns.append( col );
345  }
346  }
347  else
348  {
349  //resetting, so add all attributes to columns
350  int idx = 0;
351  for ( const QgsField &field : layerFields )
352  {
353  QString currentAlias = source->attributeDisplayName( idx );
355  col.setAttribute( field.name() );
356  col.setHeading( currentAlias );
357  mColumns.append( col );
358  idx++;
359  }
360  }
361 
362  if ( refresh )
363  {
365  }
366 }
367 
368 void QgsLayoutItemAttributeTable::restoreFieldAliasMap( const QMap<int, QString> &map )
369 {
371  if ( !source )
372  {
373  return;
374  }
375 
376  for ( int i = 0; i < mColumns.count(); i++ )
377  {
378  int attrIdx = source->fields().lookupField( mColumns[i].attribute() );
379  if ( map.contains( attrIdx ) )
380  {
381  mColumns[i].setHeading( map.value( attrIdx ) );
382  }
383  else
384  {
385  mColumns[i].setHeading( source->attributeDisplayName( attrIdx ) );
386  }
387  }
388 }
389 
391 {
392  contents.clear();
393  mLayerCache.clear();
394 
395  QgsVectorLayer *layer = sourceLayer();
396  if ( !layer )
397  {
398  //no source layer
399  return false;
400  }
401 
402  const QgsConditionalLayerStyles *conditionalStyles = layer->conditionalStyles();
403 
405  context.setFields( layer->fields() );
406 
407  QgsFeatureRequest req;
408  req.setExpressionContext( context );
409 
410  //prepare filter expression
411  std::unique_ptr<QgsExpression> filterExpression;
412  bool activeFilter = false;
413  if ( mFilterFeatures && !mFeatureFilter.isEmpty() )
414  {
415  filterExpression = std::make_unique< QgsExpression >( mFeatureFilter );
416  if ( !filterExpression->hasParserError() )
417  {
418  activeFilter = true;
419  req.setFilterExpression( mFeatureFilter );
420  }
421  }
422 
423 #ifdef HAVE_SERVER_PYTHON_PLUGINS
424  if ( mLayout->renderContext().featureFilterProvider() )
425  {
426  mLayout->renderContext().featureFilterProvider()->filterFeatures( layer, req );
427  }
428 #endif
429 
430  QgsRectangle selectionRect;
431  QgsGeometry visibleRegion;
432  std::unique_ptr< QgsGeometryEngine > visibleMapEngine;
433  if ( mMap && mShowOnlyVisibleFeatures )
434  {
435  visibleRegion = QgsGeometry::fromQPolygonF( mMap->visibleExtentPolygon() );
436  selectionRect = visibleRegion.boundingBox();
437  //transform back to layer CRS
438  const QgsCoordinateTransform coordTransform( layer->crs(), mMap->crs(), mLayout->project() );
439  QgsCoordinateTransform extentTransform = coordTransform;
440  extentTransform.setBallparkTransformsAreAppropriate( true );
441  try
442  {
443  selectionRect = extentTransform.transformBoundingBox( selectionRect, Qgis::TransformDirection::Reverse );
444  visibleRegion.transform( coordTransform, Qgis::TransformDirection::Reverse );
445  }
446  catch ( QgsCsException &cse )
447  {
448  Q_UNUSED( cse )
449  return false;
450  }
451  visibleMapEngine.reset( QgsGeometry::createGeometryEngine( visibleRegion.constGet() ) );
452  visibleMapEngine->prepareGeometry();
453  }
454 
455  QgsGeometry atlasGeometry;
456  std::unique_ptr< QgsGeometryEngine > atlasGeometryEngine;
457  if ( mFilterToAtlasIntersection )
458  {
459  atlasGeometry = mLayout->reportContext().currentGeometry( layer->crs() );
460  if ( !atlasGeometry.isNull() )
461  {
462  if ( selectionRect.isNull() )
463  {
464  selectionRect = atlasGeometry.boundingBox();
465  }
466  else
467  {
468  selectionRect = selectionRect.intersect( atlasGeometry.boundingBox() );
469  }
470 
471  atlasGeometryEngine.reset( QgsGeometry::createGeometryEngine( atlasGeometry.constGet() ) );
472  atlasGeometryEngine->prepareGeometry();
473  }
474  }
475 
477  {
478  QgsRelation relation = mLayout->project()->relationManager()->relation( mRelationId );
479  QgsFeature atlasFeature = mLayout->reportContext().feature();
480  req = relation.getRelatedFeaturesRequest( atlasFeature );
481  }
482 
483  if ( !selectionRect.isEmpty() )
484  req.setFilterRect( selectionRect );
485 
486  req.setFlags( mShowOnlyVisibleFeatures ? QgsFeatureRequest::ExactIntersect : QgsFeatureRequest::NoFlags );
487 
489  {
490  //source mode is current atlas feature
491  QgsFeature atlasFeature = mLayout->reportContext().feature();
492  req.setFilterFid( atlasFeature.id() );
493  }
494 
495  for ( const QgsLayoutTableColumn &column : std::as_const( mSortColumns ) )
496  {
497  req.addOrderBy( column.attribute(), column.sortOrder() == Qt::AscendingOrder );
498  }
499 
500  QgsFeature f;
501  int counter = 0;
502  QgsFeatureIterator fit = layer->getFeatures( req );
503 
504  mConditionalStyles.clear();
505  mFeatures.clear();
506 
507  QVector< QVector< Cell > > tempContents;
508  QgsLayoutTableContents existingContents;
509 
510  while ( fit.nextFeature( f ) && counter < mMaximumNumberOfFeatures )
511  {
512  context.setFeature( f );
513  //check feature against filter
514  if ( activeFilter && filterExpression )
515  {
516  QVariant result = filterExpression->evaluate( &context );
517  // skip this feature if the filter evaluation is false
518  if ( !result.toBool() )
519  {
520  continue;
521  }
522  }
523 
524  // check against exact map bounds
525  if ( visibleMapEngine )
526  {
527  if ( !f.hasGeometry() )
528  continue;
529 
530  if ( !visibleMapEngine->intersects( f.geometry().constGet() ) )
531  continue;
532  }
533 
534  //check against atlas feature intersection
535  if ( mFilterToAtlasIntersection )
536  {
537  if ( !f.hasGeometry() || !atlasGeometryEngine )
538  {
539  continue;
540  }
541 
542  if ( !atlasGeometryEngine->intersects( f.geometry().constGet() ) )
543  continue;
544  }
545 
546  QgsConditionalStyle rowStyle;
547 
548  if ( mUseConditionalStyling )
549  {
550  const QList<QgsConditionalStyle> styles = QgsConditionalStyle::matchingConditionalStyles( conditionalStyles->rowStyles(), QVariant(), context );
551  rowStyle = QgsConditionalStyle::compressStyles( styles );
552  }
553 
554  // We need to build up two different lists here -- one is a pair of the cell contents along with the cell style.
555  // 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
556  // correctly when this occurs
557  // We also need a list of just the cell contents, so that we can do a quick check for row uniqueness (when the
558  // corresponding option is enabled)
559  QVector< Cell > currentRow;
560 #ifdef HAVE_SERVER_PYTHON_PLUGINS
561  mColumns = filteredColumns();
562 #endif
563  currentRow.reserve( mColumns.count() );
564  QgsLayoutTableRow rowContents;
565  rowContents.reserve( mColumns.count() );
566 
567  for ( const QgsLayoutTableColumn &column : std::as_const( mColumns ) )
568  {
569  QgsConditionalStyle style;
570  int idx = layer->fields().lookupField( column.attribute() );
571  if ( idx != -1 )
572  {
573  QVariant val = f.attributes().at( idx );
574 
575  if ( mUseConditionalStyling )
576  {
577  QList<QgsConditionalStyle> styles = conditionalStyles->fieldStyles( layer->fields().at( idx ).name() );
578  styles = QgsConditionalStyle::matchingConditionalStyles( styles, val, context );
579  styles.insert( 0, rowStyle );
580  style = QgsConditionalStyle::compressStyles( styles );
581  }
582 
583  const QgsEditorWidgetSetup setup = layer->fields().at( idx ).editorWidgetSetup();
584 
585  if ( ! setup.isNull() )
586  {
588  QVariant cache;
589 
590  auto it = mLayerCache.constFind( column.attribute() );
591  if ( it != mLayerCache.constEnd() )
592  {
593  cache = it.value();
594  }
595  else
596  {
597  cache = fieldFormatter->createCache( layer, idx, setup.config() );
598  mLayerCache.insert( column.attribute(), cache );
599  }
600 
601  val = fieldFormatter->representValue( layer, idx, setup.config(), cache, val );
602  }
603 
604  QVariant v = val.isNull() ? QString() : replaceWrapChar( val );
605  currentRow << Cell( v, style, f );
606  rowContents << v;
607  }
608  else
609  {
610  // Lets assume it's an expression
611  std::unique_ptr< QgsExpression > expression = std::make_unique< QgsExpression >( column.attribute() );
612  context.lastScope()->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "row_number" ), counter + 1, true ) );
613  expression->prepare( &context );
614  QVariant value = expression->evaluate( &context );
615 
616  currentRow << Cell( value, rowStyle, f );
617  rowContents << value;
618  }
619  }
620 
621  if ( mShowUniqueRowsOnly )
622  {
623  if ( contentsContainsRow( existingContents, rowContents ) )
624  continue;
625  }
626 
627  tempContents << currentRow;
628  existingContents << rowContents;
629  ++counter;
630  }
631 
632  // build final table contents
633  contents.reserve( tempContents.size() );
634  mConditionalStyles.reserve( tempContents.size() );
635  mFeatures.reserve( tempContents.size() );
636  for ( auto it = tempContents.constBegin(); it != tempContents.constEnd(); ++it )
637  {
638  QgsLayoutTableRow row;
639  QList< QgsConditionalStyle > rowStyles;
640  row.reserve( it->size() );
641  rowStyles.reserve( it->size() );
642 
643  for ( auto cellIt = it->constBegin(); cellIt != it->constEnd(); ++cellIt )
644  {
645  row << cellIt->content;
646  rowStyles << cellIt->style;
647  if ( cellIt == it->constBegin() )
648  mFeatures << cellIt->feature;
649  }
650  contents << row;
651  mConditionalStyles << rowStyles;
652  }
653 
655  return true;
656 }
657 
659 {
660  if ( row >= mConditionalStyles.size() )
661  return QgsConditionalStyle();
662 
663  return mConditionalStyles.at( row ).at( column );
664 }
665 
667 {
669 
670  const QgsConditionalStyle style = conditionalCellStyle( row, column );
671  if ( style.isValid() )
672  {
673  // apply conditional style formatting to text format
674  const QFont styleFont = style.font();
675  if ( styleFont != QFont() )
676  {
677  QFont newFont = format.font();
678  // we want to keep all the other font settings, like word/letter spacing
679  newFont.setFamily( styleFont.family() );
680 
681  // warning -- there's a potential trap here! We can't just read QFont::styleName(), as that may be blank even when
682  // the font has the bold or italic attributes set! Reading the style name via QFontInfo avoids this and always returns
683  // a correct style name
684  const QString styleName = QgsFontUtils::resolveFontStyleName( styleFont );
685  if ( !styleName.isEmpty() )
686  newFont.setStyleName( styleName );
687 
688  newFont.setStrikeOut( styleFont.strikeOut() );
689  newFont.setUnderline( styleFont.underline() );
690  format.setFont( newFont );
691  if ( styleName.isEmpty() )
692  {
693  // we couldn't find a direct match for the conditional font's bold/italic settings as a font style name.
694  // This means the conditional style is using Qt's "faux bold/italic" mode. Even though it causes reduced quality font
695  // rendering, we'll apply it here anyway just to ensure that the rendered font styling matches the conditional style.
696  if ( styleFont.bold() )
697  format.setForcedBold( true );
698  if ( styleFont.italic() )
699  format.setForcedItalic( true );
700  }
701  }
702  }
703 
704  return format;
705 }
706 
708 {
709  std::unique_ptr< QgsExpressionContextScope >scope( QgsLayoutTable::scopeForCell( row, column ) );
710  scope->setFeature( mFeatures.value( row ) );
711  scope->setFields( scope->feature().fields() );
712  return scope.release();
713 }
714 
716 {
718 
719  if ( mSource == LayerAttributes )
720  {
721  context.appendScope( QgsExpressionContextUtils::layerScope( mVectorLayer.get() ) );
722  }
723 
724  return context;
725 }
726 
728 {
730  if ( !mMap && !mMapUuid.isEmpty() && mLayout )
731  {
732  mMap = qobject_cast< QgsLayoutItemMap *>( mLayout->itemByUuid( mMapUuid, true ) );
733  if ( mMap )
734  {
735  //if we have found a valid map item, listen out to extent changes on it and refresh the table
738  }
739  }
740 }
741 
743 {
745 
748  {
749  mDataDefinedVectorLayer = nullptr;
750 
751  QString currentLayerIdentifier;
752  if ( QgsVectorLayer *currentLayer = mVectorLayer.get() )
753  currentLayerIdentifier = currentLayer->id();
754 
755  const QString layerIdentifier = mDataDefinedProperties.valueAsString( QgsLayoutObject::AttributeTableSourceLayer, context, currentLayerIdentifier );
756  QgsVectorLayer *ddLayer = qobject_cast< QgsVectorLayer * >( QgsLayoutUtils::mapLayerFromString( layerIdentifier, mLayout->project() ) );
757  if ( ddLayer )
758  mDataDefinedVectorLayer = ddLayer;
759  }
760 
762 }
763 
764 QVariant QgsLayoutItemAttributeTable::replaceWrapChar( const QVariant &variant ) const
765 {
766  //avoid converting variants to string if not required (try to maintain original type for sorting)
767  if ( mWrapString.isEmpty() || !variant.toString().contains( mWrapString ) )
768  return variant;
769 
770  QString replaced = variant.toString();
771  replaced.replace( mWrapString, QLatin1String( "\n" ) );
772  return replaced;
773 }
774 
775 #ifdef HAVE_SERVER_PYTHON_PLUGINS
776 QgsLayoutTableColumns QgsLayoutItemAttributeTable::filteredColumns()
777 {
778 
779  QgsLayoutTableColumns allowedColumns { mColumns };
780 
781  // Filter columns
782  if ( mLayout->renderContext().featureFilterProvider() )
783  {
784 
786 
787  if ( ! source )
788  {
789  return allowedColumns;
790  }
791 
792  QHash<const QString, QSet<QString>> columnAttributesMap;
793  QSet<QString> allowedAttributes;
794 
795  for ( const auto &c : std::as_const( allowedColumns ) )
796  {
797  if ( ! c.attribute().isEmpty() && ! columnAttributesMap.contains( c.attribute() ) )
798  {
799  columnAttributesMap[ c.attribute() ] = QSet<QString>();
800  const QgsExpression columnExp { c.attribute() };
801  const auto constRefs { columnExp.findNodes<QgsExpressionNodeColumnRef>() };
802  for ( const auto &cref : constRefs )
803  {
804  columnAttributesMap[ c.attribute() ].insert( cref->name() );
805  allowedAttributes.insert( cref->name() );
806  }
807  }
808  }
809 
810  const QStringList filteredAttributes { layout()->renderContext().featureFilterProvider()->layerAttributes( source, allowedAttributes.values() ) };
811 #if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0))
812  const QSet<QString> filteredAttributesSet( filteredAttributes.constBegin(), filteredAttributes.constEnd() );
813 #else
814  const QSet<QString> filteredAttributesSet { filteredAttributes.toSet() };
815 #endif
816  if ( filteredAttributesSet != allowedAttributes )
817  {
818  const auto forbidden { allowedAttributes.subtract( filteredAttributesSet ) };
819  allowedColumns.erase( std::remove_if( allowedColumns.begin(), allowedColumns.end(), [ &columnAttributesMap, &forbidden ]( QgsLayoutTableColumn & c ) -> bool
820  {
821  for ( const auto &f : std::as_const( forbidden ) )
822  {
823  if ( columnAttributesMap[ c.attribute() ].contains( f ) )
824  {
825  return true;
826  }
827  }
828  return false;
829  } ), allowedColumns.end() );
830 
831  }
832  }
833 
834  return allowedColumns;
835 }
836 #endif
837 
839 {
840  switch ( mSource )
841  {
843  return mLayout->reportContext().layer();
845  {
846  if ( mDataDefinedVectorLayer )
847  return mDataDefinedVectorLayer;
848  else
849  return mVectorLayer.get();
850  }
852  {
853  QgsRelation relation = mLayout->project()->relationManager()->relation( mRelationId );
854  return relation.referencingLayer();
855  }
856  }
857  return nullptr;
858 }
859 
860 void QgsLayoutItemAttributeTable::removeLayer( const QString &layerId )
861 {
862  if ( mVectorLayer && mSource == QgsLayoutItemAttributeTable::LayerAttributes )
863  {
864  if ( layerId == mVectorLayer->id() )
865  {
866  mVectorLayer.setLayer( nullptr );
867  //remove existing columns
868  mColumns.clear();
869  }
870  }
871 }
872 
873 void QgsLayoutItemAttributeTable::setWrapString( const QString &wrapString )
874 {
875  if ( wrapString == mWrapString )
876  {
877  return;
878  }
879 
880  mWrapString = wrapString;
882  emit changed();
883 }
884 
885 bool QgsLayoutItemAttributeTable::writePropertiesToElement( QDomElement &tableElem, QDomDocument &doc, const QgsReadWriteContext &context ) const
886 {
887  if ( !QgsLayoutTable::writePropertiesToElement( tableElem, doc, context ) )
888  return false;
889 
890  tableElem.setAttribute( QStringLiteral( "source" ), QString::number( static_cast< int >( mSource ) ) );
891  tableElem.setAttribute( QStringLiteral( "relationId" ), mRelationId );
892  tableElem.setAttribute( QStringLiteral( "showUniqueRowsOnly" ), mShowUniqueRowsOnly );
893  tableElem.setAttribute( QStringLiteral( "showOnlyVisibleFeatures" ), mShowOnlyVisibleFeatures );
894  tableElem.setAttribute( QStringLiteral( "filterToAtlasIntersection" ), mFilterToAtlasIntersection );
895  tableElem.setAttribute( QStringLiteral( "maxFeatures" ), mMaximumNumberOfFeatures );
896  tableElem.setAttribute( QStringLiteral( "filterFeatures" ), mFilterFeatures ? QStringLiteral( "true" ) : QStringLiteral( "false" ) );
897  tableElem.setAttribute( QStringLiteral( "featureFilter" ), mFeatureFilter );
898  tableElem.setAttribute( QStringLiteral( "wrapString" ), mWrapString );
899  tableElem.setAttribute( QStringLiteral( "useConditionalStyling" ), mUseConditionalStyling );
900 
901  if ( mMap )
902  {
903  tableElem.setAttribute( QStringLiteral( "mapUuid" ), mMap->uuid() );
904  }
905 
906  if ( mVectorLayer )
907  {
908  tableElem.setAttribute( QStringLiteral( "vectorLayer" ), mVectorLayer.layerId );
909  tableElem.setAttribute( QStringLiteral( "vectorLayerName" ), mVectorLayer.name );
910  tableElem.setAttribute( QStringLiteral( "vectorLayerSource" ), mVectorLayer.source );
911  tableElem.setAttribute( QStringLiteral( "vectorLayerProvider" ), mVectorLayer.provider );
912  }
913  return true;
914 }
915 
916 bool QgsLayoutItemAttributeTable::readPropertiesFromElement( const QDomElement &itemElem, const QDomDocument &doc, const QgsReadWriteContext &context )
917 {
918  if ( QgsVectorLayer *prevLayer = sourceLayer() )
919  {
920  //disconnect from previous layer
921  disconnect( prevLayer, &QgsVectorLayer::layerModified, this, &QgsLayoutTable::refreshAttributes );
922  }
923 
924  if ( !QgsLayoutTable::readPropertiesFromElement( itemElem, doc, context ) )
925  return false;
926 
927  mSource = QgsLayoutItemAttributeTable::ContentSource( itemElem.attribute( QStringLiteral( "source" ), QStringLiteral( "0" ) ).toInt() );
928  mRelationId = itemElem.attribute( QStringLiteral( "relationId" ), QString() );
929 
931  {
932  mCurrentAtlasLayer = mLayout->reportContext().layer();
933  }
934 
935  mShowUniqueRowsOnly = itemElem.attribute( QStringLiteral( "showUniqueRowsOnly" ), QStringLiteral( "0" ) ).toInt();
936  mShowOnlyVisibleFeatures = itemElem.attribute( QStringLiteral( "showOnlyVisibleFeatures" ), QStringLiteral( "1" ) ).toInt();
937  mFilterToAtlasIntersection = itemElem.attribute( QStringLiteral( "filterToAtlasIntersection" ), QStringLiteral( "0" ) ).toInt();
938  mFilterFeatures = itemElem.attribute( QStringLiteral( "filterFeatures" ), QStringLiteral( "false" ) ) == QLatin1String( "true" );
939  mFeatureFilter = itemElem.attribute( QStringLiteral( "featureFilter" ), QString() );
940  mMaximumNumberOfFeatures = itemElem.attribute( QStringLiteral( "maxFeatures" ), QStringLiteral( "5" ) ).toInt();
941  mWrapString = itemElem.attribute( QStringLiteral( "wrapString" ) );
942  mUseConditionalStyling = itemElem.attribute( QStringLiteral( "useConditionalStyling" ), QStringLiteral( "0" ) ).toInt();
943 
944  //map
945  mMapUuid = itemElem.attribute( QStringLiteral( "mapUuid" ) );
946  if ( mMap )
947  {
950  mMap = nullptr;
951  }
952  // setting new mMap occurs in finalizeRestoreFromXml
953 
954  //vector layer
955  QString layerId = itemElem.attribute( QStringLiteral( "vectorLayer" ) );
956  QString layerName = itemElem.attribute( QStringLiteral( "vectorLayerName" ) );
957  QString layerSource = itemElem.attribute( QStringLiteral( "vectorLayerSource" ) );
958  QString layerProvider = itemElem.attribute( QStringLiteral( "vectorLayerProvider" ) );
959  mVectorLayer = QgsVectorLayerRef( layerId, layerName, layerSource, layerProvider );
960  mVectorLayer.resolveWeakly( mLayout->project() );
961 
962  //connect to new layer
963  if ( QgsVectorLayer *newLayer = sourceLayer() )
965 
967 
968  emit changed();
969  return true;
970 }
971 
973 {
974  if ( source == mSource )
975  {
976  return;
977  }
978 
979  QgsVectorLayer *prevLayer = sourceLayer();
980  mSource = source;
981  QgsVectorLayer *newLayer = sourceLayer();
982 
983  if ( newLayer != prevLayer )
984  {
985  //disconnect from previous layer
986  if ( prevLayer )
987  {
988  disconnect( prevLayer, &QgsVectorLayer::layerModified, this, &QgsLayoutTable::refreshAttributes );
989  }
990 
991  //connect to new layer
994  {
995  mCurrentAtlasLayer = newLayer;
996  }
997 
998  //layer has changed as a result of the source change, so reset column list
999  resetColumns();
1000  }
1001 
1003  emit changed();
1004 }
QString valueAsString(int key, const QgsExpressionContext &context, const QString &defaultString=QString(), bool *ok=nullptr) const
Calculates the current value of the property with the specified key and interprets it as a string.
static QIcon getThemeIcon(const QString &name, const QColor &fillColor=QColor(), const QColor &strokeColor=QColor())
Helper to get a theme icon.
static QgsFieldFormatterRegistry * fieldFormatterRegistry()
Gets the registry of available field formatters.
The QgsConditionalLayerStyles class holds conditional style information for a layer.
QgsConditionalStyles rowStyles() const
Returns a list of row styles associated with the layer.
QList< QgsConditionalStyle > fieldStyles(const QString &fieldName) const
Returns the conditional styles set for the field with matching fieldName.
Conditional styling for a rule.
static QgsConditionalStyle compressStyles(const QList< QgsConditionalStyle > &styles)
Compress a list of styles into a single style.
static QList< QgsConditionalStyle > matchingConditionalStyles(const QList< QgsConditionalStyle > &styles, const QVariant &value, QgsExpressionContext &context)
Find and return the matching styles for the value and feature.
QFont font() const
The font for the style.
bool isValid() const
isValid Check if this rule is valid.
Class for doing transforms between two map coordinate systems.
void setBallparkTransformsAreAppropriate(bool appropriate)
Sets whether approximate "ballpark" results are appropriate for this coordinate transform.
QgsRectangle transformBoundingBox(const QgsRectangle &rectangle, Qgis::TransformDirection direction=Qgis::TransformDirection::Forward, bool handle180Crossover=false) const SIP_THROW(QgsCsException)
Transforms a rectangle from the source CRS to the destination CRS.
Custom exception class for Coordinate Reference System related exceptions.
Definition: qgsexception.h:66
Holder for the widget type and its configuration for a field.
QVariantMap config() const
Single scope for storing variables and functions for use within a QgsExpressionContext.
void addVariable(const QgsExpressionContextScope::StaticVariable &variable)
Adds a variable into the context scope.
static QgsExpressionContextScope * layerScope(const QgsMapLayer *layer)
Creates a new scope which contains variables and functions relating to a QgsMapLayer.
Expression contexts are used to encapsulate the parameters around which a QgsExpression should be eva...
QgsExpressionContextScope * lastScope()
Returns the last scope added to the context.
void appendScope(QgsExpressionContextScope *scope)
Appends a scope to the end of the context.
void setFeature(const QgsFeature &feature)
Convenience function for setting a feature for the context.
void setFields(const QgsFields &fields)
Convenience function for setting a fields for the context.
An expression node which takes it value from a feature's field.
Class for parsing and evaluation of expressions (formerly called "search strings").
virtual QStringList layerAttributes(const QgsVectorLayer *layer, const QStringList &attributes) const =0
Returns the list of visible attribute names from a list of attributes names for the given layer.
Wrapper for iterator of features from vector data provider or vector layer.
bool nextFeature(QgsFeature &f)
This class wraps a request for features to a vector layer (or directly its vector data provider).
QgsFeatureRequest & setFlags(QgsFeatureRequest::Flags flags)
Sets flags that affect how features will be fetched.
QgsFeatureRequest & addOrderBy(const QString &expression, bool ascending=true)
Adds a new OrderByClause, appending it as the least important one.
QgsFeatureRequest & setFilterExpression(const QString &expression)
Set the filter expression.
@ ExactIntersect
Use exact geometry intersection (slower) instead of bounding boxes.
QgsFeatureRequest & setExpressionContext(const QgsExpressionContext &context)
Sets the expression context used to evaluate filter expressions.
QgsFeatureRequest & setFilterFid(QgsFeatureId fid)
Sets the feature ID that should be fetched.
QgsFeatureRequest & setFilterRect(const QgsRectangle &rectangle)
Sets the rectangle from which features will be taken.
The feature class encapsulates a single feature including its unique ID, geometry and a list of field...
Definition: qgsfeature.h:56
QgsAttributes attributes
Definition: qgsfeature.h:65
QgsGeometry geometry
Definition: qgsfeature.h:67
bool hasGeometry() const
Returns true if the feature has an associated geometry.
Definition: qgsfeature.cpp: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
static QString resolveFontStyleName(const QFont &font)
Attempts to resolve the style name corresponding to the specified font object.
A geometry is the spatial representation of a feature.
Definition: qgsgeometry.h: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.
QgsTextFormat textFormatForCell(int row, int column) const override
Returns the text format to use for the cell at the specified row and column.
static QgsLayoutItemAttributeTable * create(QgsLayout *layout)
Returns a new QgsLayoutItemAttributeTable for the specified parent layout.
void setVectorLayer(QgsVectorLayer *layer)
Sets the vector layer from which to display feature attributes.
void setSource(ContentSource source)
Sets the source for attributes to show in table body.
Layout graphical items for displaying a map.
void extentChanged()
Emitted when the map's extent changes.
void mapRotationChanged(double newRotation)
Emitted when the map's rotation changes.
QPolygonF visibleExtentPolygon() const
Returns a polygon representing the current visible map extent, considering map extents and rotation.
QgsCoordinateReferenceSystem crs() const
Returns coordinate reference system used for rendering the map.
@ LayoutAttributeTable
Attribute table.
virtual QString uuid() const
Returns the item identification string.
virtual void finalizeRestoreFromXml()
Called after all pending items have been restored from XML.
QgsExpressionContext createExpressionContext() const override
This method needs to be reimplemented in all classes which implement this interface and return an exp...
virtual void refreshDataDefinedProperty(QgsLayoutObject::DataDefinedProperty property=QgsLayoutObject::AllProperties)
Refreshes a data defined property for the multi frame by reevaluating the property's value and redraw...
QgsPropertyCollection mDataDefinedProperties
const QgsLayout * layout() const
Returns the layout the object is attached to.
void changed()
Emitted when the object's properties change.
QPointer< QgsLayout > mLayout
DataDefinedProperty
Data defined properties for different item types.
@ AttributeTableSourceLayer
Attribute table source layer.
@ AllProperties
All properties for item.
QgsFeatureFilterProvider * featureFilterProvider() const
Returns the possibly NULL feature filter provider.
void layerChanged(QgsVectorLayer *layer)
Emitted when the context's layer is changed.
Stores properties of a column for a QgsLayoutTable.
void setAttribute(const QString &attribute)
Sets the attribute name or expression used for the column's values.
void setHeading(const QString &heading)
Sets the heading for a column, which is the value displayed in the column's header cell.
A class to display a table in the print layout, and allow the table to span over multiple frames.
virtual void refreshAttributes()
Refreshes the contents shown in the table by querying for new data.
void recalculateTableSize()
Recalculates and updates the size of the table and all table frames.
virtual QgsExpressionContextScope * scopeForCell(int row, int column) const
Creates a new QgsExpressionContextScope for the cell at row, column.
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.
QgsTextFormat mContentTextFormat
bool writePropertiesToElement(QDomElement &elem, QDomDocument &doc, const QgsReadWriteContext &context) const override
Stores multiframe state within an XML DOM element.
QgsLayoutTableSortColumns mSortColumns
Columns to sort the table.
bool readPropertiesFromElement(const QDomElement &itemElem, const QDomDocument &doc, const QgsReadWriteContext &context) override
Sets multiframe state from a DOM element.
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:104
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...
Container for all settings relating to text rendering.
Definition: qgstextformat.h:41
void setFont(const QFont &font)
Sets the font used for rendering text.
void setForcedItalic(bool forced)
Sets whether the format is set to force an italic style.
void setForcedBold(bool forced)
Sets whether the format is set to force a bold style.
QFont font() const
Returns the font used for rendering text.
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.