QGIS API Documentation  3.24.2-Tisler (13c1a02865)
qgsreportsectionfieldgroup.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgsreportsectionfieldgroup.cpp
3  --------------------
4  begin : December 2017
5  copyright : (C) 2017 by Nyall Dawson
6  email : nyall dot dawson at gmail dot com
7  ***************************************************************************/
8 /***************************************************************************
9  * *
10  * This program is free software; you can redistribute it and/or modify *
11  * it under the terms of the GNU General Public License as published by *
12  * the Free Software Foundation; either version 2 of the License, or *
13  * (at your option) any later version. *
14  * *
15  ***************************************************************************/
16 
18 #include "qgslayout.h"
19 
21 
22 QgsReportSectionFieldGroup::QgsReportSectionFieldGroup( QgsAbstractReportSection *parent )
23  : QgsAbstractReportSection( parent )
24 {
25 
26 }
27 
28 QString QgsReportSectionFieldGroup::description() const
29 {
30  if ( mCoverageLayer.get() )
31  return QObject::tr( "Group: %1 - %2" ).arg( mCoverageLayer->name(), mField );
32  else
33  return QObject::tr( "Group" );
34 }
35 
36 QIcon QgsReportSectionFieldGroup::icon() const
37 {
38  return QgsApplication::getThemeIcon( QStringLiteral( "/mIconFieldText.svg" ) );
39 }
40 
41 QgsReportSectionFieldGroup *QgsReportSectionFieldGroup::clone() const
42 {
43  std::unique_ptr< QgsReportSectionFieldGroup > copy = std::make_unique< QgsReportSectionFieldGroup >( nullptr );
44  copyCommonProperties( copy.get() );
45 
46  if ( mBody )
47  {
48  copy->mBody.reset( mBody->clone() );
49  }
50  else
51  copy->mBody.reset();
52 
53  copy->setLayer( mCoverageLayer.get() );
54  copy->setField( mField );
55  copy->setSortAscending( mSortAscending );
56  copy->setBodyEnabled( mBodyEnabled );
57 
58  return copy.release();
59 }
60 
61 bool QgsReportSectionFieldGroup::beginRender()
62 {
63  if ( !mCoverageLayer.get() )
64  return false;
65 
66  if ( !mField.isEmpty() )
67  {
68  mFieldIndex = mCoverageLayer->fields().lookupField( mField );
69  if ( mFieldIndex < 0 )
70  return false;
71 
72  if ( mBody )
73  mBody->reportContext().setLayer( mCoverageLayer.get() );
74 
75  mFeatures = QgsFeatureIterator();
76  }
77  return QgsAbstractReportSection::beginRender();
78 }
79 
80 bool QgsReportSectionFieldGroup::prepareHeader()
81 {
82  if ( !header() )
83  return false;
84 
85  if ( !mFeatures.isValid() )
86  {
87  mFeatures = mCoverageLayer->getFeatures( buildFeatureRequest() );
88  }
89 
90  mHeaderFeature = getNextFeature();
91  header()->reportContext().blockSignals( true );
92  header()->reportContext().setLayer( mCoverageLayer.get() );
93  header()->reportContext().blockSignals( false );
94  header()->reportContext().setFeature( mHeaderFeature );
95  mSkipNextRequest = true;
96  mNoFeatures = !mHeaderFeature.isValid();
97  return mHeaderVisibility == AlwaysInclude || !mNoFeatures;
98 }
99 
100 bool QgsReportSectionFieldGroup::prepareFooter()
101 {
102  return mFooterVisibility == AlwaysInclude || !mNoFeatures;
103 }
104 
105 QgsLayout *QgsReportSectionFieldGroup::nextBody( bool &ok )
106 {
107  if ( !mFeatures.isValid() )
108  {
109  mFeatures = mCoverageLayer->getFeatures( buildFeatureRequest() );
110  }
111 
112  QgsFeature f;
113  if ( !mSkipNextRequest )
114  {
115  f = getNextFeature();
116  }
117  else
118  {
119  f = mHeaderFeature;
120  mSkipNextRequest = false;
121  }
122 
123  if ( !f.isValid() )
124  {
125  // no features left for this iteration
126  mFeatures = QgsFeatureIterator();
127 
128  if ( auto *lFooter = footer() )
129  {
130  lFooter->reportContext().blockSignals( true );
131  lFooter->reportContext().setLayer( mCoverageLayer.get() );
132  lFooter->reportContext().blockSignals( false );
133  lFooter->reportContext().setFeature( mLastFeature );
134  }
135  ok = false;
136  return nullptr;
137  }
138 
139  mLastFeature = f;
140 
141  updateChildContexts( f );
142 
143  ok = true;
144  if ( mBody && mBodyEnabled )
145  {
146  mBody->reportContext().blockSignals( true );
147  mBody->reportContext().setLayer( mCoverageLayer.get() );
148  mBody->reportContext().blockSignals( false );
149  mBody->reportContext().setFeature( f );
150  }
151 
152  return mBodyEnabled ? mBody.get() : nullptr;
153 }
154 
155 void QgsReportSectionFieldGroup::reset()
156 {
157  QgsAbstractReportSection::reset();
158  mEncounteredValues.clear();
159  mSkipNextRequest = false;
160  mHeaderFeature = QgsFeature();
161  mLastFeature = QgsFeature();
162  mFeatures = QgsFeatureIterator();
163  mNoFeatures = false;
164 }
165 
166 void QgsReportSectionFieldGroup::setParentSection( QgsAbstractReportSection *parent )
167 {
168  QgsAbstractReportSection::setParentSection( parent );
169  if ( !mCoverageLayer )
170  mCoverageLayer.resolveWeakly( project() );
171 }
172 
173 void QgsReportSectionFieldGroup::reloadSettings()
174 {
175  QgsAbstractReportSection::reloadSettings();
176  if ( mBody )
177  mBody->reloadSettings();
178 }
179 
180 bool QgsReportSectionFieldGroup::writePropertiesToElement( QDomElement &element, QDomDocument &doc, const QgsReadWriteContext &context ) const
181 {
182  element.setAttribute( QStringLiteral( "headerVisibility" ), static_cast< int >( mHeaderVisibility ) );
183  element.setAttribute( QStringLiteral( "footerVisibility" ), static_cast< int >( mFooterVisibility ) );
184  element.setAttribute( QStringLiteral( "field" ), mField );
185  element.setAttribute( QStringLiteral( "ascending" ), mSortAscending ? QStringLiteral( "1" ) : QStringLiteral( "0" ) );
186  element.setAttribute( QStringLiteral( "bodyEnabled" ), mBodyEnabled ? QStringLiteral( "1" ) : QStringLiteral( "0" ) );
187  if ( mCoverageLayer )
188  {
189  element.setAttribute( QStringLiteral( "coverageLayer" ), mCoverageLayer.layerId );
190  element.setAttribute( QStringLiteral( "coverageLayerName" ), mCoverageLayer.name );
191  element.setAttribute( QStringLiteral( "coverageLayerSource" ), mCoverageLayer.source );
192  element.setAttribute( QStringLiteral( "coverageLayerProvider" ), mCoverageLayer.provider );
193  }
194 
195  if ( mBody )
196  {
197  QDomElement bodyElement = doc.createElement( QStringLiteral( "body" ) );
198  bodyElement.appendChild( mBody->writeXml( doc, context ) );
199  element.appendChild( bodyElement );
200  }
201  return true;
202 }
203 
204 bool QgsReportSectionFieldGroup::readPropertiesFromElement( const QDomElement &element, const QDomDocument &doc, const QgsReadWriteContext &context )
205 {
206  mHeaderVisibility = static_cast< SectionVisibility >( element.attribute( QStringLiteral( "headerVisibility" ) ).toInt() );
207  mFooterVisibility = static_cast< SectionVisibility >( element.attribute( QStringLiteral( "footerVisibility" ) ).toInt() );
208  mField = element.attribute( QStringLiteral( "field" ) );
209  mSortAscending = element.attribute( QStringLiteral( "ascending" ) ).toInt();
210  mBodyEnabled = element.attribute( QStringLiteral( "bodyEnabled" ) ).toInt();
211  QString layerId = element.attribute( QStringLiteral( "coverageLayer" ) );
212  QString layerName = element.attribute( QStringLiteral( "coverageLayerName" ) );
213  QString layerSource = element.attribute( QStringLiteral( "coverageLayerSource" ) );
214  QString layerProvider = element.attribute( QStringLiteral( "coverageLayerProvider" ) );
215  mCoverageLayer = QgsVectorLayerRef( layerId, layerName, layerSource, layerProvider );
216  mCoverageLayer.resolveWeakly( project() );
217 
218  const QDomElement bodyElement = element.firstChildElement( QStringLiteral( "body" ) );
219  if ( !bodyElement.isNull() )
220  {
221  const QDomElement bodyLayoutElem = bodyElement.firstChild().toElement();
222  std::unique_ptr< QgsLayout > body = std::make_unique< QgsLayout >( project() );
223  body->readXml( bodyLayoutElem, doc, context );
224  mBody = std::move( body );
225  }
226  return true;
227 }
228 
229 bool QgsReportSectionFieldGroup::sortAscending() const
230 {
231  return mSortAscending;
232 }
233 
234 void QgsReportSectionFieldGroup::setSortAscending( bool sortAscending )
235 {
236  mSortAscending = sortAscending;
237 }
238 
239 QgsFeatureRequest QgsReportSectionFieldGroup::buildFeatureRequest() const
240 {
241  QgsFeatureRequest request;
242  QVariantMap filter = context().fieldFilters;
243 
244  QStringList filterParts;
245  for ( auto filterIt = filter.constBegin(); filterIt != filter.constEnd(); ++filterIt )
246  {
247  // use lookupField since we don't want case sensitivity
248  int fieldIndex = mCoverageLayer->fields().lookupField( filterIt.key() );
249  if ( fieldIndex >= 0 )
250  {
251  // layer has a matching field, so we need to filter by it
252  filterParts << QgsExpression::createFieldEqualityExpression( mCoverageLayer->fields().at( fieldIndex ).name(), filterIt.value() );
253  }
254  }
255  if ( !filterParts.empty() )
256  {
257  QString filterString = QStringLiteral( "(%1)" ).arg( filterParts.join( QLatin1String( ") AND (" ) ) );
258  request.setFilterExpression( filterString );
259  }
260 
261  request.addOrderBy( mField, mSortAscending );
262  return request;
263 }
264 
265 QgsFeature QgsReportSectionFieldGroup::getNextFeature()
266 {
267  QgsFeature f;
268  QVariant currentValue;
269  bool first = true;
270  while ( first || ( ( !mBody || !mBodyEnabled ) && mEncounteredValues.contains( currentValue ) ) )
271  {
272  if ( !mFeatures.nextFeature( f ) )
273  {
274  return QgsFeature();
275  }
276 
277  first = false;
278  currentValue = f.attribute( mFieldIndex );
279  }
280 
281  mEncounteredValues.insert( currentValue );
282  return f;
283 }
284 
285 void QgsReportSectionFieldGroup::updateChildContexts( const QgsFeature &feature )
286 {
287  QgsReportSectionContext c = context();
288  c.feature = feature;
289  if ( mCoverageLayer )
290  c.currentLayer = mCoverageLayer.get();
291 
292  QVariantMap currentFilter = c.fieldFilters;
293  currentFilter.insert( mField, feature.attribute( mFieldIndex ) );
294  c.fieldFilters = currentFilter;
295 
296  const QList< QgsAbstractReportSection * > sections = childSections();
297  for ( QgsAbstractReportSection *section : std::as_const( sections ) )
298  {
299  section->setContext( c );
300  }
301 }
302 
304 
static QIcon getThemeIcon(const QString &name, const QColor &fillColor=QColor(), const QColor &strokeColor=QColor())
Helper to get a theme icon.
static QString createFieldEqualityExpression(const QString &fieldName, const QVariant &value, QVariant::Type fieldType=QVariant::Type::Invalid)
Create an expression allowing to evaluate if a field is equal to a value.
Wrapper for iterator of features from vector data provider or vector layer.
This class wraps a request for features to a vector layer (or directly its vector data provider).
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.
The feature class encapsulates a single feature including its unique ID, geometry and a list of field...
Definition: qgsfeature.h:56
bool isValid() const
Returns the validity of this feature.
Definition: qgsfeature.cpp:209
QVariant attribute(const QString &name) const
Lookup attribute value by attribute name.
Definition: qgsfeature.cpp:320
Base class for layouts, which can contain items such as maps, labels, scalebars, etc.
Definition: qgslayout.h:51
The class is used as a container of context for various read/write operations on other objects.
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
_LayerRef< QgsVectorLayer > QgsVectorLayerRef