Skip to content

Commit f86c298

Browse files
committedJan 5, 2018
Serialize atlas settings
1 parent 83af352 commit f86c298

File tree

10 files changed

+220
-10
lines changed

10 files changed

+220
-10
lines changed
 

‎python/core/layout/qgslayout.sip

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -500,14 +500,14 @@ If ``ok`` is specified, it will be set to true if the load was successful.
500500
Returns a list of loaded items.
501501
%End
502502

503-
QDomElement writeXml( QDomDocument &document, const QgsReadWriteContext &context ) const;
503+
virtual QDomElement writeXml( QDomDocument &document, const QgsReadWriteContext &context ) const;
504504
%Docstring
505505
Returns the layout's state encapsulated in a DOM element.
506506

507507
.. seealso:: :py:func:`readXml()`
508508
%End
509509

510-
bool readXml( const QDomElement &layoutElement, const QDomDocument &document, const QgsReadWriteContext &context );
510+
virtual bool readXml( const QDomElement &layoutElement, const QDomDocument &document, const QgsReadWriteContext &context );
511511
%Docstring
512512
Sets the collection's state from a DOM element. ``layoutElement`` is the DOM node corresponding to the layout.
513513

‎python/core/layout/qgslayoutatlas.sip

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88

99

1010

11-
class QgsLayoutAtlas : QObject
11+
class QgsLayoutAtlas : QObject, QgsLayoutSerializableObject
1212
{
1313
%Docstring
1414
Class used to render an Atlas, iterating over geometry features.
@@ -34,6 +34,15 @@ QgsLayoutAtlas which is automatically created and attached to the composition.
3434
Constructor for new QgsLayoutAtlas.
3535
%End
3636

37+
virtual QString stringType() const;
38+
39+
virtual QgsLayout *layout();
40+
41+
virtual bool writeXml( QDomElement &parentElement, QDomDocument &document, const QgsReadWriteContext &context ) const;
42+
43+
virtual bool readXml( const QDomElement &element, const QDomDocument &document, const QgsReadWriteContext &context );
44+
45+
3746
bool enabled() const;
3847
%Docstring
3948
Returns whether the atlas generation is enabled
@@ -72,7 +81,7 @@ atlas page.
7281
.. seealso:: :py:func:`filenameExpressionErrorString()`
7382
%End
7483

75-
bool setFilenameExpression( const QString &expression, QString &errorString );
84+
bool setFilenameExpression( const QString &expression, QString &errorString /Out/ );
7685
%Docstring
7786
Sets the filename ``expression`` used for generating output filenames for each
7887
atlas page.
@@ -222,7 +231,7 @@ This property has no effect is filterFeatures() is false.
222231
.. seealso:: :py:func:`filterFeatures()`
223232
%End
224233

225-
bool setFilterExpression( const QString &expression, QString &errorString );
234+
bool setFilterExpression( const QString &expression, QString &errorString /Out/ );
226235
%Docstring
227236
Sets the ``expression`` used for filtering features in the coverage layer.
228237

‎python/core/layout/qgsprintlayout.sip

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,11 @@ Constructor for QgsPrintLayout.
3131
Returns the print layout's atlas.
3232
%End
3333

34+
virtual QDomElement writeXml( QDomDocument &document, const QgsReadWriteContext &context ) const;
35+
36+
virtual bool readXml( const QDomElement &layoutElement, const QDomDocument &document, const QgsReadWriteContext &context );
37+
38+
3439
};
3540

3641
/************************************************************************

‎src/core/layout/qgslayout.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -514,13 +514,13 @@ class CORE_EXPORT QgsLayout : public QGraphicsScene, public QgsExpressionContext
514514
* Returns the layout's state encapsulated in a DOM element.
515515
* \see readXml()
516516
*/
517-
QDomElement writeXml( QDomDocument &document, const QgsReadWriteContext &context ) const;
517+
virtual QDomElement writeXml( QDomDocument &document, const QgsReadWriteContext &context ) const;
518518

519519
/**
520520
* Sets the collection's state from a DOM element. \a layoutElement is the DOM node corresponding to the layout.
521521
* \see writeXml()
522522
*/
523-
bool readXml( const QDomElement &layoutElement, const QDomDocument &document, const QgsReadWriteContext &context );
523+
virtual bool readXml( const QDomElement &layoutElement, const QDomDocument &document, const QgsReadWriteContext &context );
524524

525525
/**
526526
* Add items from an XML representation to the layout. Used for project file reading and pasting items from clipboard.

‎src/core/layout/qgslayoutatlas.cpp

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,84 @@ QgsLayoutAtlas::QgsLayoutAtlas( QgsLayout *layout )
3131
connect( mLayout->project(), static_cast < void ( QgsProject::* )( const QStringList & ) >( &QgsProject::layersWillBeRemoved ), this, &QgsLayoutAtlas::removeLayers );
3232
}
3333

34+
QString QgsLayoutAtlas::stringType() const
35+
{
36+
return QStringLiteral( "atlas" );
37+
}
38+
39+
QgsLayout *QgsLayoutAtlas::layout()
40+
{
41+
return mLayout;
42+
}
43+
44+
bool QgsLayoutAtlas::writeXml( QDomElement &parentElement, QDomDocument &document, const QgsReadWriteContext & ) const
45+
{
46+
QDomElement atlasElem = document.createElement( QStringLiteral( "Atlas" ) );
47+
atlasElem.setAttribute( QStringLiteral( "enabled" ), mEnabled ? QStringLiteral( "1" ) : QStringLiteral( "0" ) );
48+
49+
if ( mCoverageLayer )
50+
{
51+
atlasElem.setAttribute( QStringLiteral( "coverageLayer" ), mCoverageLayer.layerId );
52+
atlasElem.setAttribute( QStringLiteral( "coverageLayerName" ), mCoverageLayer.name );
53+
atlasElem.setAttribute( QStringLiteral( "coverageLayerSource" ), mCoverageLayer.source );
54+
atlasElem.setAttribute( QStringLiteral( "coverageLayerProvider" ), mCoverageLayer.provider );
55+
}
56+
else
57+
{
58+
atlasElem.setAttribute( QStringLiteral( "coverageLayer" ), QString() );
59+
}
60+
61+
atlasElem.setAttribute( QStringLiteral( "hideCoverage" ), mHideCoverage ? QStringLiteral( "1" ) : QStringLiteral( "0" ) );
62+
atlasElem.setAttribute( QStringLiteral( "filenamePattern" ), mFilenameExpressionString );
63+
atlasElem.setAttribute( QStringLiteral( "pageNameExpression" ), mPageNameExpression );
64+
65+
atlasElem.setAttribute( QStringLiteral( "sortFeatures" ), mSortFeatures ? QStringLiteral( "1" ) : QStringLiteral( "0" ) );
66+
if ( mSortFeatures )
67+
{
68+
atlasElem.setAttribute( QStringLiteral( "sortKey" ), mSortExpression );
69+
atlasElem.setAttribute( QStringLiteral( "sortAscending" ), mSortAscending ? QStringLiteral( "1" ) : QStringLiteral( "0" ) );
70+
}
71+
atlasElem.setAttribute( QStringLiteral( "filterFeatures" ), mFilterFeatures ? QStringLiteral( "1" ) : QStringLiteral( "0" ) );
72+
if ( mFilterFeatures )
73+
{
74+
atlasElem.setAttribute( QStringLiteral( "featureFilter" ), mFilterExpression );
75+
}
76+
77+
parentElement.appendChild( atlasElem );
78+
79+
return true;
80+
}
81+
82+
bool QgsLayoutAtlas::readXml( const QDomElement &atlasElem, const QDomDocument &, const QgsReadWriteContext & )
83+
{
84+
mEnabled = atlasElem.attribute( QStringLiteral( "enabled" ), QStringLiteral( "0" ) ).toInt();
85+
86+
// look for stored layer name
87+
QString layerId = atlasElem.attribute( QStringLiteral( "coverageLayer" ) );
88+
QString layerName = atlasElem.attribute( QStringLiteral( "coverageLayerName" ) );
89+
QString layerSource = atlasElem.attribute( QStringLiteral( "coverageLayerSource" ) );
90+
QString layerProvider = atlasElem.attribute( QStringLiteral( "coverageLayerProvider" ) );
91+
92+
mCoverageLayer = QgsVectorLayerRef( layerId, layerName, layerSource, layerProvider );
93+
mCoverageLayer.resolveWeakly( mLayout->project() );
94+
95+
mPageNameExpression = atlasElem.attribute( QStringLiteral( "pageNameExpression" ), QString() );
96+
QString error;
97+
setFilenameExpression( atlasElem.attribute( QStringLiteral( "filenamePattern" ), QString() ), error );
98+
99+
mSortFeatures = atlasElem.attribute( QStringLiteral( "sortFeatures" ), QStringLiteral( "0" ) ).toInt();
100+
mSortExpression = atlasElem.attribute( QStringLiteral( "sortKey" ) );
101+
mSortAscending = atlasElem.attribute( QStringLiteral( "sortAscending" ), QStringLiteral( "1" ) ).toInt();
102+
mFilterFeatures = atlasElem.attribute( QStringLiteral( "filterFeatures" ), QStringLiteral( "0" ) ).toInt();
103+
mFilterExpression = atlasElem.attribute( QStringLiteral( "featureFilter" ) );
104+
105+
mHideCoverage = atlasElem.attribute( QStringLiteral( "hideCoverage" ), QStringLiteral( "0" ) ).toInt();
106+
107+
emit toggled( mEnabled );
108+
emit changed();
109+
return true;
110+
}
111+
34112
void QgsLayoutAtlas::setEnabled( bool enabled )
35113
{
36114
if ( enabled == mEnabled )

‎src/core/layout/qgslayoutatlas.h

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818

1919
#include "qgis_core.h"
2020
#include "qgsvectorlayerref.h"
21+
#include "qgslayoutserializableobject.h"
2122
#include <QObject>
2223

2324
class QgsLayout;
@@ -32,7 +33,7 @@ class QgsLayout;
3233
* QgsLayoutAtlas which is automatically created and attached to the composition.
3334
* \since QGIS 3.0
3435
*/
35-
class CORE_EXPORT QgsLayoutAtlas : public QObject
36+
class CORE_EXPORT QgsLayoutAtlas : public QObject, public QgsLayoutSerializableObject
3637
{
3738
Q_OBJECT
3839
public:
@@ -42,6 +43,11 @@ class CORE_EXPORT QgsLayoutAtlas : public QObject
4243
*/
4344
QgsLayoutAtlas( QgsLayout *layout SIP_TRANSFERTHIS );
4445

46+
QString stringType() const override;
47+
QgsLayout *layout() override;
48+
bool writeXml( QDomElement &parentElement, QDomDocument &document, const QgsReadWriteContext &context ) const override;
49+
bool readXml( const QDomElement &element, const QDomDocument &document, const QgsReadWriteContext &context ) override;
50+
4551
/**
4652
* Returns whether the atlas generation is enabled
4753
* \see setEnabled()
@@ -81,7 +87,7 @@ class CORE_EXPORT QgsLayoutAtlas : public QObject
8187
* will be set to the expression error.
8288
* \see filenameExpression()
8389
*/
84-
bool setFilenameExpression( const QString &expression, QString &errorString );
90+
bool setFilenameExpression( const QString &expression, QString &errorString SIP_OUT );
8591

8692
/**
8793
* Returns the coverage layer used for the atlas features.
@@ -209,7 +215,7 @@ class CORE_EXPORT QgsLayoutAtlas : public QObject
209215
* \see filterExpression()
210216
* \see setFilterFeatures()
211217
*/
212-
bool setFilterExpression( const QString &expression, QString &errorString );
218+
bool setFilterExpression( const QString &expression, QString &errorString SIP_OUT );
213219

214220
public slots:
215221

‎src/core/layout/qgsprintlayout.cpp

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,3 +27,20 @@ QgsLayoutAtlas *QgsPrintLayout::atlas()
2727
{
2828
return mAtlas;
2929
}
30+
31+
QDomElement QgsPrintLayout::writeXml( QDomDocument &document, const QgsReadWriteContext &context ) const
32+
{
33+
QDomElement layoutElem = QgsLayout::writeXml( document, context );
34+
mAtlas->writeXml( layoutElem, document, context );
35+
return layoutElem;
36+
}
37+
38+
bool QgsPrintLayout::readXml( const QDomElement &layoutElement, const QDomDocument &document, const QgsReadWriteContext &context )
39+
{
40+
if ( !QgsLayout::readXml( layoutElement, document, context ) )
41+
return false;
42+
43+
QDomElement atlasElem = layoutElement.firstChildElement( QStringLiteral( "Atlas" ) );
44+
mAtlas->readXml( atlasElem, document, context );
45+
return true;
46+
}

‎src/core/layout/qgsprintlayout.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,9 @@ class CORE_EXPORT QgsPrintLayout : public QgsLayout
4343
*/
4444
QgsLayoutAtlas *atlas();
4545

46+
QDomElement writeXml( QDomDocument &document, const QgsReadWriteContext &context ) const override;
47+
bool readXml( const QDomElement &layoutElement, const QDomDocument &document, const QgsReadWriteContext &context ) override;
48+
4649
private:
4750

4851
QgsLayoutAtlas *mAtlas = nullptr;

‎tests/src/python/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,7 @@ ADD_PYTHON_TEST(PyQgsLayerTreeMapCanvasBridge test_qgslayertreemapcanvasbridge.p
8484
ADD_PYTHON_TEST(PyQgsLayerTree test_qgslayertree.py)
8585
ADD_PYTHON_TEST(PyQgsLayout test_qgslayout.py)
8686
ADD_PYTHON_TEST(PyQgsLayoutAlign test_qgslayoutaligner.py)
87+
ADD_PYTHON_TEST(PyQgsLayoutAtlas test_qgslayoutatlas.py)
8788
ADD_PYTHON_TEST(PyQgsLayoutExporter test_qgslayoutexporter.py)
8889
ADD_PYTHON_TEST(PyQgsLayoutFrame test_qgslayoutframe.py)
8990
ADD_PYTHON_TEST(PyQgsLayoutManager test_qgslayoutmanager.py)
Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
# -*- coding: utf-8 -*-
2+
"""QGIS Unit tests for QgsLayoutAtlas
3+
4+
.. note:: This program is free software; you can redistribute it and/or modify
5+
it under the terms of the GNU General Public License as published by
6+
the Free Software Foundation; either version 2 of the License, or
7+
(at your option) any later version.
8+
"""
9+
__author__ = 'Nyall Dawson'
10+
__date__ = '19/12/2017'
11+
__copyright__ = 'Copyright 2017, The QGIS Project'
12+
# This will get replaced with a git SHA1 when you do a git archive
13+
__revision__ = '$Format:%H$'
14+
15+
import qgis # NOQA
16+
import sip
17+
import tempfile
18+
import shutil
19+
import os
20+
21+
from qgis.core import (QgsUnitTypes,
22+
QgsLayout,
23+
QgsPrintLayout,
24+
QgsLayoutAtlas,
25+
QgsLayoutItemPage,
26+
QgsLayoutGuide,
27+
QgsLayoutObject,
28+
QgsProject,
29+
QgsLayoutItemGroup,
30+
QgsLayoutItem,
31+
QgsProperty,
32+
QgsLayoutPageCollection,
33+
QgsLayoutMeasurement,
34+
QgsFillSymbol,
35+
QgsReadWriteContext,
36+
QgsLayoutItemMap,
37+
QgsLayoutItemLabel,
38+
QgsLayoutSize,
39+
QgsLayoutPoint,
40+
QgsVectorLayer)
41+
from qgis.PyQt.QtCore import QFileInfo
42+
from qgis.PyQt.QtTest import QSignalSpy
43+
from qgis.PyQt.QtXml import QDomDocument
44+
from utilities import unitTestDataPath
45+
from qgis.testing import start_app, unittest
46+
47+
start_app()
48+
49+
50+
class TestQgsLayoutAtlas(unittest.TestCase):
51+
52+
def testReadWriteXml(self):
53+
p = QgsProject()
54+
vectorFileInfo = QFileInfo(unitTestDataPath() + "/france_parts.shp")
55+
vector_layer = QgsVectorLayer(vectorFileInfo.filePath(), vectorFileInfo.completeBaseName(), "ogr")
56+
self.assertTrue(vector_layer.isValid())
57+
p.addMapLayer(vector_layer)
58+
59+
l = QgsPrintLayout(p)
60+
atlas = l.atlas()
61+
atlas.setEnabled(True)
62+
atlas.setHideCoverage(True)
63+
atlas.setFilenameExpression('filename exp')
64+
atlas.setCoverageLayer(vector_layer)
65+
atlas.setPageNameExpression('page name')
66+
atlas.setSortFeatures(True)
67+
atlas.setSortAscending(False)
68+
atlas.setSortExpression('sort exp')
69+
atlas.setFilterFeatures(True)
70+
atlas.setFilterExpression('filter exp')
71+
72+
doc = QDomDocument("testdoc")
73+
elem = l.writeXml(doc, QgsReadWriteContext())
74+
75+
l2 = QgsPrintLayout(p)
76+
self.assertTrue(l2.readXml(elem, doc, QgsReadWriteContext()))
77+
atlas2 = l2.atlas()
78+
self.assertTrue(atlas2.enabled())
79+
self.assertTrue(atlas2.hideCoverage())
80+
self.assertEqual(atlas2.filenameExpression(), 'filename exp')
81+
self.assertEqual(atlas2.coverageLayer(), vector_layer)
82+
self.assertEqual(atlas2.pageNameExpression(), 'page name')
83+
self.assertTrue(atlas2.sortFeatures())
84+
self.assertFalse(atlas2.sortAscending())
85+
self.assertEqual(atlas2.sortExpression(), 'sort exp')
86+
self.assertTrue(atlas2.filterFeatures())
87+
self.assertEqual(atlas2.filterExpression(), 'filter exp')
88+
89+
90+
if __name__ == '__main__':
91+
unittest.main()

0 commit comments

Comments
 (0)
Please sign in to comment.