QGIS API Documentation 3.28.0-Firenze (ed3ad0430f)
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
22QgsReportSectionFieldGroup::QgsReportSectionFieldGroup( QgsAbstractReportSection *parent )
23 : QgsAbstractReportSection( parent )
24{
25
26}
27
28QString 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
36QIcon QgsReportSectionFieldGroup::icon() const
37{
38 return QgsApplication::getThemeIcon( QStringLiteral( "/mIconFieldText.svg" ) );
39}
40
41QgsReportSectionFieldGroup *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
61bool 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
80bool 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
100bool QgsReportSectionFieldGroup::prepareFooter()
101{
102 return mFooterVisibility == AlwaysInclude || !mNoFeatures;
103}
104
105QgsLayout *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
155void 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
166void QgsReportSectionFieldGroup::setParentSection( QgsAbstractReportSection *parent )
167{
168 QgsAbstractReportSection::setParentSection( parent );
169 if ( !mCoverageLayer )
170 mCoverageLayer.resolveWeakly( project() );
171}
172
173void QgsReportSectionFieldGroup::reloadSettings()
174{
175 QgsAbstractReportSection::reloadSettings();
176 if ( mBody )
177 mBody->reloadSettings();
178}
179
180bool 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
204bool 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
229bool QgsReportSectionFieldGroup::sortAscending() const
230{
231 return mSortAscending;
232}
233
234void QgsReportSectionFieldGroup::setSortAscending( bool sortAscending )
235{
236 mSortAscending = sortAscending;
237}
238
239QgsFeatureRequest 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
265QgsFeature 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
285void 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:219
QVariant attribute(const QString &name) const
Lookup attribute value by attribute name.
Definition: qgsfeature.cpp:338
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