Skip to content

Commit e169c21

Browse files
committedJan 5, 2018
Work on modernizing atlas
1 parent d62bc35 commit e169c21

10 files changed

+602
-8
lines changed
 

‎python/core/core_auto.sip

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,7 @@
161161
%Include composer/qgscomposermultiframecommand.sip
162162
%Include composer/qgscomposertexttable.sip
163163
%Include composer/qgspaperitem.sip
164+
%Include layout/qgsabstractlayoutiterator.sip
164165
%Include layout/qgslayoutaligner.sip
165166
%Include layout/qgslayoutexporter.sip
166167
%Include layout/qgslayoutgridsettings.sip
Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
/************************************************************************
2+
* This file has been generated automatically from *
3+
* *
4+
* src/core/layout/qgsabstractlayoutiterator.h *
5+
* *
6+
* Do not edit manually ! Edit header and run scripts/sipify.pl again *
7+
************************************************************************/
8+
9+
10+
11+
class QgsAbstractLayoutIterator
12+
{
13+
%Docstring
14+
*************************************************************************
15+
*
16+
This program is free software; you can redistribute it and/or modify *
17+
it under the terms of the GNU General Public License as published by *
18+
the Free Software Foundation; either version 2 of the License, or *
19+
(at your option) any later version. *
20+
*
21+
**************************************************************************
22+
%End
23+
24+
%TypeHeaderCode
25+
#include "qgsabstractlayoutiterator.h"
26+
%End
27+
public:
28+
29+
virtual ~QgsAbstractLayoutIterator();
30+
31+
virtual bool beginRender() = 0;
32+
%Docstring
33+
Called when rendering begins, before iteration commences. Returns true if successful, false if no iteration
34+
is available or required.
35+
36+
.. seealso:: :py:func:`endRender()`
37+
%End
38+
39+
virtual bool endRender() = 0;
40+
%Docstring
41+
Ends the render, performing any required cleanup tasks.
42+
%End
43+
44+
virtual int count() const = 0;
45+
%Docstring
46+
Returns the number of features to iterate over.
47+
%End
48+
49+
virtual bool next() = 0;
50+
%Docstring
51+
Iterates to next feature, returning false if no more features exist to iterate over.
52+
53+
.. seealso:: :py:func:`previous()`
54+
55+
.. seealso:: :py:func:`last()`
56+
57+
.. seealso:: :py:func:`first()`
58+
%End
59+
60+
virtual bool previous() = 0;
61+
%Docstring
62+
Iterates to the previous feature, returning false if no previous feature exists.
63+
64+
.. seealso:: :py:func:`next()`
65+
66+
.. seealso:: :py:func:`last()`
67+
68+
.. seealso:: :py:func:`first()`
69+
%End
70+
71+
virtual bool last() = 0;
72+
%Docstring
73+
Seeks to the last feature, returning false if no feature was found.
74+
75+
.. seealso:: :py:func:`next()`
76+
77+
.. seealso:: :py:func:`previous()`
78+
79+
.. seealso:: :py:func:`first()`
80+
%End
81+
82+
virtual bool first() = 0;
83+
%Docstring
84+
Seeks to the first feature, returning false if no feature was found.
85+
86+
.. seealso:: :py:func:`next()`
87+
88+
.. seealso:: :py:func:`previous()`
89+
90+
.. seealso:: :py:func:`last()`
91+
%End
92+
};
93+
94+
95+
96+
97+
/************************************************************************
98+
* This file has been generated automatically from *
99+
* *
100+
* src/core/layout/qgsabstractlayoutiterator.h *
101+
* *
102+
* Do not edit manually ! Edit header and run scripts/sipify.pl again *
103+
************************************************************************/

‎python/core/layout/qgslayoutatlas.sip

Lines changed: 43 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88

99

1010

11-
class QgsLayoutAtlas : QObject, QgsLayoutSerializableObject
11+
class QgsLayoutAtlas : QObject, QgsAbstractLayoutIterator, QgsLayoutSerializableObject
1212
{
1313
%Docstring
1414
Class used to render an Atlas, iterating over geometry features.
@@ -245,8 +245,30 @@ will be set to the expression error.
245245
.. seealso:: :py:func:`setFilterFeatures()`
246246
%End
247247

248+
int updateFeatures();
249+
%Docstring
250+
Requeries the current atlas coverage layer and applies filtering and sorting. Returns
251+
number of matching features.
252+
%End
253+
254+
virtual bool beginRender();
255+
256+
virtual bool endRender();
257+
258+
virtual int count() const;
259+
260+
248261
public slots:
249262

263+
virtual bool next();
264+
265+
virtual bool previous();
266+
267+
virtual bool first();
268+
269+
virtual bool last();
270+
271+
250272
signals:
251273

252274
void changed();
@@ -262,6 +284,26 @@ Emitted when atlas is enabled or disabled.
262284
void coverageLayerChanged( QgsVectorLayer *layer );
263285
%Docstring
264286
Emitted when the coverage layer for the atlas changes.
287+
%End
288+
289+
void messagePushed( const QString &message );
290+
%Docstring
291+
Is emitted when the atlas has an updated status bar ``message``.
292+
%End
293+
294+
void numberFeaturesChanged( int numFeatures );
295+
%Docstring
296+
Emitted when the number of features for the atlas changes.
297+
%End
298+
299+
void renderBegun();
300+
%Docstring
301+
Emitted when atlas rendering has begun.
302+
%End
303+
304+
void renderEnded();
305+
%Docstring
306+
Emitted when atlas rendering has ended.
265307
%End
266308

267309
};

‎python/core/layout/qgslayoutcontext.sip

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,8 @@ Sets the current ``feature`` for evaluating the layout. This feature may
9090
be used for altering an item's content and appearance for a report
9191
or atlas layout.
9292

93+
Emits the changed() signal.
94+
9395
.. seealso:: :py:func:`feature()`
9496
%End
9597

@@ -113,6 +115,8 @@ Returns the vector layer associated with the layout's context.
113115
%Docstring
114116
Sets the vector ``layer`` associated with the layout's context.
115117

118+
Emits the changed() signal.
119+
116120
.. seealso:: :py:func:`layer()`
117121
%End
118122

@@ -220,6 +224,12 @@ If ``layer`` is -1, all item layers should be rendered.
220224
Emitted whenever the context's ``flags`` change.
221225

222226
.. seealso:: :py:func:`setFlags()`
227+
%End
228+
229+
void changed();
230+
%Docstring
231+
Emitted certain settings in the context is changed, e.g. by setting a new feature or vector layer
232+
for the context.
223233
%End
224234

225235
};

‎src/core/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1026,6 +1026,7 @@ SET(QGIS_CORE_HDRS
10261026
composer/qgscomposertexttable.h
10271027
composer/qgspaperitem.h
10281028

1029+
layout/qgsabstractlayoutiterator.h
10291030
layout/qgslayoutaligner.h
10301031
layout/qgslayoutexporter.h
10311032
layout/qgslayoutgridsettings.h
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
/***************************************************************************
2+
qgsabstractlayoutiterator.h
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+
#ifndef QGSABSTRACTLAYOUTITERATOR_H
17+
#define QGSABSTRACTLAYOUTITERATOR_H
18+
19+
#include "qgis_core.h"
20+
21+
22+
class CORE_EXPORT QgsAbstractLayoutIterator
23+
{
24+
25+
public:
26+
27+
virtual ~QgsAbstractLayoutIterator() = default;
28+
29+
/**
30+
* Called when rendering begins, before iteration commences. Returns true if successful, false if no iteration
31+
* is available or required.
32+
* \see endRender()
33+
*/
34+
virtual bool beginRender() = 0;
35+
36+
/**
37+
* Ends the render, performing any required cleanup tasks.
38+
*/
39+
virtual bool endRender() = 0;
40+
41+
/**
42+
* Returns the number of features to iterate over.
43+
*/
44+
virtual int count() const = 0;
45+
46+
/**
47+
* Iterates to next feature, returning false if no more features exist to iterate over.
48+
* \see previous()
49+
* \see last()
50+
* \see first()
51+
*/
52+
virtual bool next() = 0;
53+
54+
/**
55+
* Iterates to the previous feature, returning false if no previous feature exists.
56+
* \see next()
57+
* \see last()
58+
* \see first()
59+
*/
60+
virtual bool previous() = 0;
61+
62+
/**
63+
* Seeks to the last feature, returning false if no feature was found.
64+
* \see next()
65+
* \see previous()
66+
* \see first()
67+
*/
68+
virtual bool last() = 0;
69+
70+
/**
71+
* Seeks to the first feature, returning false if no feature was found.
72+
* \see next()
73+
* \see previous()
74+
* \see last()
75+
*/
76+
virtual bool first() = 0;
77+
};
78+
79+
#endif //QGSABSTRACTLAYOUTITERATOR_H
80+
81+
82+

‎src/core/layout/qgslayoutatlas.cpp

Lines changed: 294 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020

2121
#include "qgslayoutatlas.h"
2222
#include "qgslayout.h"
23+
#include "qgsmessagelog.h"
2324

2425
QgsLayoutAtlas::QgsLayoutAtlas( QgsLayout *layout )
2526
: QObject( layout )
@@ -153,12 +154,10 @@ void QgsLayoutAtlas::setCoverageLayer( QgsVectorLayer *layer )
153154

154155
QString QgsLayoutAtlas::nameForPage( int pageNumber ) const
155156
{
156-
#if 0 //TODO
157157
if ( pageNumber < 0 || pageNumber >= mFeatureIds.count() )
158158
return QString();
159159

160160
return mFeatureIds.at( pageNumber ).second;
161-
#endif
162161
}
163162

164163
bool QgsLayoutAtlas::setFilterExpression( const QString &expression, QString &errorString )
@@ -167,11 +166,12 @@ bool QgsLayoutAtlas::setFilterExpression( const QString &expression, QString &er
167166
return true;
168167
}
169168

169+
170170
/// @cond PRIVATE
171-
class AtlasFieldSorter
171+
class AtlasFeatureSorter
172172
{
173173
public:
174-
AtlasFieldSorter( QgsLayoutAtlas::SorterKeys &keys, bool ascending = true )
174+
AtlasFeatureSorter( QgsLayoutAtlas::SorterKeys &keys, bool ascending = true )
175175
: mKeys( keys )
176176
, mAscending( ascending )
177177
{}
@@ -189,6 +189,180 @@ class AtlasFieldSorter
189189

190190
/// @endcond
191191

192+
int QgsLayoutAtlas::updateFeatures()
193+
{
194+
if ( !mCoverageLayer )
195+
{
196+
return 0;
197+
}
198+
199+
QgsExpressionContext expressionContext = createExpressionContext();
200+
201+
QString error;
202+
updateFilenameExpression( error );
203+
204+
// select all features with all attributes
205+
QgsFeatureRequest req;
206+
207+
mFilterParserError.clear();
208+
if ( mFilterFeatures && !mFilterExpression.isEmpty() )
209+
{
210+
QgsExpression filterExpression( mFilterExpression );
211+
if ( filterExpression.hasParserError() )
212+
{
213+
mFilterParserError = filterExpression.parserErrorString();
214+
return 0;
215+
}
216+
217+
//filter good to go
218+
req.setFilterExpression( mFilterExpression );
219+
}
220+
221+
QgsFeatureIterator fit = mCoverageLayer->getFeatures( req );
222+
223+
std::unique_ptr<QgsExpression> nameExpression;
224+
if ( !mPageNameExpression.isEmpty() )
225+
{
226+
nameExpression = qgis::make_unique< QgsExpression >( mPageNameExpression );
227+
if ( nameExpression->hasParserError() )
228+
{
229+
nameExpression.reset( nullptr );
230+
}
231+
else
232+
{
233+
nameExpression->prepare( &expressionContext );
234+
}
235+
}
236+
237+
// We cannot use nextFeature() directly since the feature pointer is rewinded by the rendering process
238+
// We thus store the feature ids for future extraction
239+
QgsFeature feat;
240+
mFeatureIds.clear();
241+
mFeatureKeys.clear();
242+
243+
std::unique_ptr<QgsExpression> sortExpression;
244+
if ( mSortFeatures && !mSortExpression.isEmpty() )
245+
{
246+
sortExpression = qgis::make_unique< QgsExpression >( mSortExpression );
247+
if ( sortExpression->hasParserError() )
248+
{
249+
sortExpression.reset( nullptr );
250+
}
251+
else
252+
{
253+
sortExpression->prepare( &expressionContext );
254+
}
255+
}
256+
257+
while ( fit.nextFeature( feat ) )
258+
{
259+
expressionContext.setFeature( feat );
260+
261+
QString pageName;
262+
if ( nameExpression )
263+
{
264+
QVariant result = nameExpression->evaluate( &expressionContext );
265+
if ( nameExpression->hasEvalError() )
266+
{
267+
QgsMessageLog::logMessage( tr( "Atlas name eval error: %1" ).arg( nameExpression->evalErrorString() ), tr( "Layout" ) );
268+
}
269+
pageName = result.toString();
270+
}
271+
272+
mFeatureIds.push_back( qMakePair( feat.id(), pageName ) );
273+
274+
if ( sortExpression )
275+
{
276+
QVariant result = sortExpression->evaluate( &expressionContext );
277+
if ( sortExpression->hasEvalError() )
278+
{
279+
QgsMessageLog::logMessage( tr( "Atlas sort eval error: %1" ).arg( sortExpression->evalErrorString() ), tr( "Layout" ) );
280+
}
281+
mFeatureKeys.insert( feat.id(), result );
282+
}
283+
}
284+
285+
// sort features, if asked for
286+
if ( !mFeatureKeys.isEmpty() )
287+
{
288+
AtlasFeatureSorter sorter( mFeatureKeys, mSortAscending );
289+
std::sort( mFeatureIds.begin(), mFeatureIds.end(), sorter );
290+
}
291+
292+
emit numberFeaturesChanged( mFeatureIds.size() );
293+
294+
#if 0 //TODO - move to app
295+
//jump to first feature if currently using an atlas preview
296+
//need to do this in case filtering/layer change has altered matching features
297+
return first();
298+
#endif
299+
300+
301+
return mFeatureIds.size();
302+
}
303+
304+
bool QgsLayoutAtlas::beginRender()
305+
{
306+
if ( !mCoverageLayer )
307+
{
308+
return false;
309+
}
310+
311+
emit renderBegun();
312+
313+
if ( !updateFeatures() )
314+
{
315+
//no matching features found
316+
return false;
317+
}
318+
319+
return true;
320+
}
321+
322+
bool QgsLayoutAtlas::endRender()
323+
{
324+
emit renderEnded();
325+
return true;
326+
}
327+
328+
int QgsLayoutAtlas::count() const
329+
{
330+
return mFeatureIds.size();
331+
}
332+
333+
bool QgsLayoutAtlas::next()
334+
{
335+
int newFeatureNo = mCurrentFeatureNo + 1;
336+
if ( newFeatureNo >= mFeatureIds.size() )
337+
{
338+
return false;
339+
}
340+
341+
return prepareForFeature( newFeatureNo );
342+
}
343+
344+
bool QgsLayoutAtlas::previous()
345+
{
346+
int newFeatureNo = mCurrentFeatureNo - 1;
347+
if ( newFeatureNo < 0 )
348+
{
349+
return false;
350+
}
351+
352+
return prepareForFeature( newFeatureNo );
353+
}
354+
355+
bool QgsLayoutAtlas::first()
356+
{
357+
return prepareForFeature( 0 );
358+
}
359+
360+
bool QgsLayoutAtlas::last()
361+
{
362+
return prepareForFeature( mFeatureIds.size() - 1 );
363+
}
364+
365+
192366
void QgsLayoutAtlas::setHideCoverage( bool hide )
193367
{
194368
mHideCoverage = hide;
@@ -265,3 +439,119 @@ bool QgsLayoutAtlas::updateFilenameExpression( QString &error )
265439
return true;
266440
}
267441

442+
bool QgsLayoutAtlas::evalFeatureFilename( const QgsExpressionContext &context )
443+
{
444+
//generate filename for current atlas feature
445+
if ( !mFilenameExpressionString.isEmpty() && mFilenameExpression.isValid() )
446+
{
447+
QVariant filenameRes = mFilenameExpression.evaluate( &context );
448+
if ( mFilenameExpression.hasEvalError() )
449+
{
450+
QgsMessageLog::logMessage( tr( "Atlas filename evaluation error: %1" ).arg( mFilenameExpression.evalErrorString() ), tr( "Composer" ) );
451+
return false;
452+
}
453+
454+
mCurrentFilename = filenameRes.toString();
455+
}
456+
return true;
457+
}
458+
459+
bool QgsLayoutAtlas::prepareForFeature( const int featureI )
460+
{
461+
if ( !mCoverageLayer )
462+
{
463+
return false;
464+
}
465+
466+
if ( mFeatureIds.isEmpty() )
467+
{
468+
emit messagePushed( tr( "No matching atlas features" ) );
469+
return false;
470+
}
471+
472+
if ( featureI >= mFeatureIds.size() )
473+
{
474+
return false;
475+
}
476+
477+
mCurrentFeatureNo = featureI;
478+
479+
// retrieve the next feature, based on its id
480+
if ( !mCoverageLayer->getFeatures( QgsFeatureRequest().setFilterFid( mFeatureIds[ featureI ].first ) ).nextFeature( mCurrentFeature ) )
481+
return false;
482+
483+
QgsExpressionContext expressionContext = createExpressionContext();
484+
485+
// generate filename for current feature
486+
if ( !evalFeatureFilename( expressionContext ) )
487+
{
488+
489+
//error evaluating filename
490+
return false;
491+
}
492+
493+
mGeometryCache.clear();
494+
495+
mLayout->context().blockSignals( true ); // setFeature emits changed, we don't want 2 signals
496+
mLayout->context().setLayer( mCoverageLayer.get() );
497+
mLayout->context().blockSignals( false );
498+
mLayout->context().setFeature( mCurrentFeature );
499+
500+
emit messagePushed( QString( tr( "Atlas feature %1 of %2" ) ).arg( featureI + 1 ).arg( mFeatureIds.size() ) );
501+
502+
if ( !mCurrentFeature.isValid() )
503+
{
504+
//bad feature
505+
return false;
506+
}
507+
508+
#if 0 //TODO - move to map
509+
//update composer maps
510+
511+
//build a list of atlas-enabled composer maps
512+
QList<QgsComposerMap *> maps;
513+
QList<QgsComposerMap *> atlasMaps;
514+
mComposition->composerItems( maps );
515+
if ( maps.isEmpty() )
516+
{
517+
return true;
518+
}
519+
for ( QList<QgsComposerMap *>::iterator mit = maps.begin(); mit != maps.end(); ++mit )
520+
{
521+
QgsComposerMap *currentMap = ( *mit );
522+
if ( !currentMap->atlasDriven() )
523+
{
524+
continue;
525+
}
526+
atlasMaps << currentMap;
527+
}
528+
529+
if ( !atlasMaps.isEmpty() )
530+
{
531+
//clear the transformed bounds of the previous feature
532+
mTransformedFeatureBounds = QgsRectangle();
533+
534+
// compute extent of current feature in the map CRS. This should be set on a per-atlas map basis,
535+
// but given that it's not currently possible to have maps with different CRSes we can just
536+
// calculate it once based on the first atlas maps' CRS.
537+
computeExtent( atlasMaps[0] );
538+
}
539+
540+
for ( QList<QgsComposerMap *>::iterator mit = maps.begin(); mit != maps.end(); ++mit )
541+
{
542+
if ( ( *mit )->atlasDriven() )
543+
{
544+
// map is atlas driven, so update it's bounds (causes a redraw)
545+
prepareMap( *mit );
546+
}
547+
else
548+
{
549+
// map is not atlas driven, so manually force a redraw (to reflect possibly atlas
550+
// dependent symbology)
551+
( *mit )->invalidateCache();
552+
}
553+
}
554+
#endif
555+
return true;
556+
}
557+

‎src/core/layout/qgslayoutatlas.h

Lines changed: 48 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
#include "qgis_core.h"
2020
#include "qgsvectorlayerref.h"
2121
#include "qgslayoutserializableobject.h"
22+
#include "qgsabstractlayoutiterator.h"
2223
#include <QObject>
2324

2425
class QgsLayout;
@@ -33,7 +34,7 @@ class QgsLayout;
3334
* QgsLayoutAtlas which is automatically created and attached to the composition.
3435
* \since QGIS 3.0
3536
*/
36-
class CORE_EXPORT QgsLayoutAtlas : public QObject, public QgsLayoutSerializableObject
37+
class CORE_EXPORT QgsLayoutAtlas : public QObject, public QgsAbstractLayoutIterator, public QgsLayoutSerializableObject
3738
{
3839
Q_OBJECT
3940
public:
@@ -217,8 +218,23 @@ class CORE_EXPORT QgsLayoutAtlas : public QObject, public QgsLayoutSerializableO
217218
*/
218219
bool setFilterExpression( const QString &expression, QString &errorString SIP_OUT );
219220

221+
/**
222+
* Requeries the current atlas coverage layer and applies filtering and sorting. Returns
223+
* number of matching features.
224+
*/
225+
int updateFeatures();
226+
227+
bool beginRender() override;
228+
bool endRender() override;
229+
int count() const override;
230+
220231
public slots:
221232

233+
bool next() override;
234+
bool previous() override;
235+
bool first() override;
236+
bool last() override;
237+
222238
signals:
223239

224240
//! Emitted when one of the atlas parameters changes.
@@ -230,6 +246,20 @@ class CORE_EXPORT QgsLayoutAtlas : public QObject, public QgsLayoutSerializableO
230246
//! Emitted when the coverage layer for the atlas changes.
231247
void coverageLayerChanged( QgsVectorLayer *layer );
232248

249+
//! Is emitted when the atlas has an updated status bar \a message.
250+
void messagePushed( const QString &message );
251+
252+
/**
253+
* Emitted when the number of features for the atlas changes.
254+
*/
255+
void numberFeaturesChanged( int numFeatures );
256+
257+
//! Emitted when atlas rendering has begun.
258+
void renderBegun();
259+
260+
//! Emitted when atlas rendering has ended.
261+
void renderEnded();
262+
233263
private slots:
234264
void removeLayers( const QStringList &layers );
235265

@@ -247,6 +277,13 @@ class CORE_EXPORT QgsLayoutAtlas : public QObject, public QgsLayoutSerializableO
247277
*/
248278
bool evalFeatureFilename( const QgsExpressionContext &context );
249279

280+
/**
281+
* Prepare the atlas for the given feature. Sets the extent and context variables
282+
* \param i feature number
283+
* \returns true if feature was successfully prepared
284+
*/
285+
bool prepareForFeature( int i );
286+
250287
QPointer< QgsLayout > mLayout;
251288

252289
bool mEnabled = false;
@@ -273,9 +310,18 @@ class CORE_EXPORT QgsLayoutAtlas : public QObject, public QgsLayoutSerializableO
273310

274311
QString mFilterParserError;
275312

313+
// id of each iterated feature (after filtering and sorting) paired with atlas page name
314+
QVector< QPair<QgsFeatureId, QString> > mFeatureIds;
315+
// current atlas feature number
316+
int mCurrentFeatureNo = 0;
317+
QgsFeature mCurrentFeature;
318+
319+
// projected geometry cache
320+
mutable QMap<long, QgsGeometry> mGeometryCache;
321+
276322
QgsExpressionContext createExpressionContext();
277323

278-
friend class AtlasFieldSorter;
324+
friend class AtlasFeatureSorter;
279325
};
280326

281327
#endif //QGSLAYOUTATLAS_H

‎src/core/layout/qgslayoutcontext.cpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,12 @@ QgsRenderContext::Flags QgsLayoutContext::renderContextFlags() const
6969
return flags;
7070
}
7171

72+
void QgsLayoutContext::setFeature( const QgsFeature &feature )
73+
{
74+
mFeature = feature;
75+
emit changed();
76+
}
77+
7278
QgsVectorLayer *QgsLayoutContext::layer() const
7379
{
7480
return mLayer;
@@ -77,6 +83,7 @@ QgsVectorLayer *QgsLayoutContext::layer() const
7783
void QgsLayoutContext::setLayer( QgsVectorLayer *layer )
7884
{
7985
mLayer = layer;
86+
emit changed();
8087
}
8188

8289
void QgsLayoutContext::setDpi( double dpi )

‎src/core/layout/qgslayoutcontext.h

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -93,9 +93,12 @@ class CORE_EXPORT QgsLayoutContext : public QObject
9393
* Sets the current \a feature for evaluating the layout. This feature may
9494
* be used for altering an item's content and appearance for a report
9595
* or atlas layout.
96+
*
97+
* Emits the changed() signal.
98+
*
9699
* \see feature()
97100
*/
98-
void setFeature( const QgsFeature &feature ) { mFeature = feature; }
101+
void setFeature( const QgsFeature &feature );
99102

100103
/**
101104
* Returns the current feature for evaluating the layout. This feature may
@@ -113,6 +116,9 @@ class CORE_EXPORT QgsLayoutContext : public QObject
113116

114117
/**
115118
* Sets the vector \a layer associated with the layout's context.
119+
*
120+
* Emits the changed() signal.
121+
*
116122
* \see layer()
117123
*/
118124
void setLayer( QgsVectorLayer *layer );
@@ -219,6 +225,12 @@ class CORE_EXPORT QgsLayoutContext : public QObject
219225
*/
220226
void flagsChanged( QgsLayoutContext::Flags flags );
221227

228+
/**
229+
* Emitted certain settings in the context is changed, e.g. by setting a new feature or vector layer
230+
* for the context.
231+
*/
232+
void changed();
233+
222234
private:
223235

224236
Flags mFlags = nullptr;

0 commit comments

Comments
 (0)
Please sign in to comment.