Skip to content

Commit 79a4694

Browse files
committed
Add support for adding/removing pages to a collection
1 parent ea32391 commit 79a4694

File tree

6 files changed

+289
-2
lines changed

6 files changed

+289
-2
lines changed

python/core/layout/qgslayoutitem.sip

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,24 @@ class QgsLayoutItem : QgsLayoutObject, QGraphicsRectItem
1818

1919
%TypeHeaderCode
2020
#include "qgslayoutitem.h"
21+
22+
#include <qgslayoutitemshape.h>
23+
#include <qgslayoutitempage.h>
24+
%End
25+
26+
%ConvertToSubClassCode
27+
// the conversions have to be static, because they're using multiple inheritance
28+
// (seen in PyQt4 .sip files for some QGraphicsItem classes)
29+
switch ( sipCpp->type() )
30+
{
31+
// really, these *should* use the constants from QgsLayoutItemRegistry, but sip doesn't like that!
32+
case QGraphicsItem::UserType + 101:
33+
sipType = sipType_QgsLayoutItemPage;
34+
*sipCppRet = static_cast<QgsLayoutItemPage *>( sipCpp );
35+
break;
36+
default:
37+
sipType = 0;
38+
}
2139
%End
2240
public:
2341

python/core/layout/qgslayoutpagecollection.sip

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,12 +26,72 @@ class QgsLayoutPageCollection : QObject
2626
Constructor for QgsLayoutItemPage, with the specified parent ``layout``.
2727
%End
2828

29+
~QgsLayoutPageCollection();
30+
2931
QgsLayout *layout() const;
3032
%Docstring
3133
Returns the layout this collection belongs to.
3234
:rtype: QgsLayout
3335
%End
3436

37+
QList< QgsLayoutItemPage * > pages();
38+
%Docstring
39+
Returns a list of pages in the collection.
40+
.. seealso:: page()
41+
.. seealso:: pageCount()
42+
:rtype: list of QgsLayoutItemPage
43+
%End
44+
45+
int pageCount() const;
46+
%Docstring
47+
Returns the number of pages in the collection.
48+
.. seealso:: pages()
49+
:rtype: int
50+
%End
51+
52+
QgsLayoutItemPage *page( int pageNumber );
53+
%Docstring
54+
Returns a specific page (by ``pageNumber``) from the collection.
55+
Internal page numbering starts at 0 - so a ``pageNumber`` of 0
56+
corresponds to the first page in the collection.
57+
A None is returned if an invalid page number is specified.
58+
.. seealso:: pages()
59+
:rtype: QgsLayoutItemPage
60+
%End
61+
62+
void addPage( QgsLayoutItemPage *page /Transfer/ );
63+
%Docstring
64+
Adds a ``page`` to the collection. Ownership of the ``page`` is transferred
65+
to the collection, and the page will automatically be added to the collection's
66+
layout() (there is no need to manually add the page item to the layout).
67+
The page will be added after all pages currently contained in the collection.
68+
.. seealso:: insertPage()
69+
%End
70+
71+
void insertPage( QgsLayoutItemPage *page /Transfer/, int beforePage );
72+
%Docstring
73+
Inserts a ``page`` into a specific position in the collection.
74+
75+
Ownership of the ``page`` is transferred
76+
to the collection, and the page will automatically be added to the collection's
77+
layout() (there is no need to manually add the page item to the layout).
78+
79+
The page will be added after before the page number specified by ``beforePage``.
80+
(Page numbers in collections begin at 0 - so a ``beforePage`` of 0 will insert
81+
the page before all existing pages).
82+
83+
.. seealso:: addPage()
84+
%End
85+
86+
void deletePage( int pageNumber );
87+
%Docstring
88+
Deletes a page from the collection. The page will automatically be removed
89+
from the collection's layout().
90+
91+
Page numbers in collections begin at 0 - so a ``pageNumber`` of 0 will delete
92+
the first page in the collection.
93+
%End
94+
3595
void setPageStyleSymbol( QgsFillSymbol *symbol );
3696
%Docstring
3797
Sets the ``symbol`` to use for drawing pages in the collection.

src/core/layout/qgslayoutitem.h

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,28 @@ class QPainter;
3535
*/
3636
class CORE_EXPORT QgsLayoutItem : public QgsLayoutObject, public QGraphicsRectItem
3737
{
38+
#ifdef SIP_RUN
39+
#include <qgslayoutitemshape.h>
40+
#include <qgslayoutitempage.h>
41+
#endif
42+
43+
44+
#ifdef SIP_RUN
45+
SIP_CONVERT_TO_SUBCLASS_CODE
46+
// the conversions have to be static, because they're using multiple inheritance
47+
// (seen in PyQt4 .sip files for some QGraphicsItem classes)
48+
switch ( sipCpp->type() )
49+
{
50+
// really, these *should* use the constants from QgsLayoutItemRegistry, but sip doesn't like that!
51+
case QGraphicsItem::UserType + 101:
52+
sipType = sipType_QgsLayoutItemPage;
53+
*sipCppRet = static_cast<QgsLayoutItemPage *>( sipCpp );
54+
break;
55+
default:
56+
sipType = 0;
57+
}
58+
SIP_END
59+
#endif
3860

3961
Q_OBJECT
4062

src/core/layout/qgslayoutpagecollection.cpp

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,15 @@ QgsLayoutPageCollection::QgsLayoutPageCollection( QgsLayout *layout )
2424
createDefaultPageStyleSymbol();
2525
}
2626

27+
QgsLayoutPageCollection::~QgsLayoutPageCollection()
28+
{
29+
Q_FOREACH ( QgsLayoutItemPage *page, mPages )
30+
{
31+
mLayout->removeItem( page );
32+
page->deleteLater();
33+
}
34+
}
35+
2736
void QgsLayoutPageCollection::setPageStyleSymbol( QgsFillSymbol *symbol )
2837
{
2938
if ( !symbol )
@@ -37,6 +46,53 @@ QgsLayout *QgsLayoutPageCollection::layout() const
3746
return mLayout;
3847
}
3948

49+
QList<QgsLayoutItemPage *> QgsLayoutPageCollection::pages()
50+
{
51+
return mPages;
52+
}
53+
54+
int QgsLayoutPageCollection::pageCount() const
55+
{
56+
return mPages.count();
57+
}
58+
59+
QgsLayoutItemPage *QgsLayoutPageCollection::page( int pageNumber )
60+
{
61+
return mPages.value( pageNumber );
62+
}
63+
64+
void QgsLayoutPageCollection::addPage( QgsLayoutItemPage *page )
65+
{
66+
mPages.append( page );
67+
mLayout->addItem( page );
68+
}
69+
70+
void QgsLayoutPageCollection::insertPage( QgsLayoutItemPage *page, int beforePage )
71+
{
72+
if ( beforePage < 0 )
73+
beforePage = 0;
74+
75+
if ( beforePage >= mPages.count() )
76+
{
77+
mPages.append( page );
78+
}
79+
else
80+
{
81+
mPages.insert( beforePage, page );
82+
}
83+
mLayout->addItem( page );
84+
}
85+
86+
void QgsLayoutPageCollection::deletePage( int pageNumber )
87+
{
88+
if ( pageNumber < 0 || pageNumber >= mPages.count() )
89+
return;
90+
91+
QgsLayoutItemPage *page = mPages.takeAt( pageNumber );
92+
mLayout->removeItem( page );
93+
page->deleteLater();
94+
}
95+
4096
void QgsLayoutPageCollection::createDefaultPageStyleSymbol()
4197
{
4298
QgsStringMap properties;

src/core/layout/qgslayoutpagecollection.h

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
#include "qgis_core.h"
2121
#include "qgis_sip.h"
2222
#include "qgssymbol.h"
23+
#include "qgslayoutitempage.h"
2324
#include <QObject>
2425
#include <memory>
2526

@@ -43,11 +44,68 @@ class CORE_EXPORT QgsLayoutPageCollection : public QObject
4344
*/
4445
explicit QgsLayoutPageCollection( QgsLayout *layout SIP_TRANSFERTHIS );
4546

47+
~QgsLayoutPageCollection();
48+
4649
/**
4750
* Returns the layout this collection belongs to.
4851
*/
4952
QgsLayout *layout() const;
5053

54+
/**
55+
* Returns a list of pages in the collection.
56+
* \see page()
57+
* \see pageCount()
58+
*/
59+
QList< QgsLayoutItemPage * > pages();
60+
61+
/**
62+
* Returns the number of pages in the collection.
63+
* \see pages()
64+
*/
65+
int pageCount() const;
66+
67+
/**
68+
* Returns a specific page (by \a pageNumber) from the collection.
69+
* Internal page numbering starts at 0 - so a \a pageNumber of 0
70+
* corresponds to the first page in the collection.
71+
* A nullptr is returned if an invalid page number is specified.
72+
* \see pages()
73+
*/
74+
QgsLayoutItemPage *page( int pageNumber );
75+
76+
/**
77+
* Adds a \a page to the collection. Ownership of the \a page is transferred
78+
* to the collection, and the page will automatically be added to the collection's
79+
* layout() (there is no need to manually add the page item to the layout).
80+
* The page will be added after all pages currently contained in the collection.
81+
* \see insertPage()
82+
*/
83+
void addPage( QgsLayoutItemPage *page SIP_TRANSFER );
84+
85+
/**
86+
* Inserts a \a page into a specific position in the collection.
87+
*
88+
* Ownership of the \a page is transferred
89+
* to the collection, and the page will automatically be added to the collection's
90+
* layout() (there is no need to manually add the page item to the layout).
91+
*
92+
* The page will be added after before the page number specified by \a beforePage.
93+
* (Page numbers in collections begin at 0 - so a \a beforePage of 0 will insert
94+
* the page before all existing pages).
95+
*
96+
* \see addPage()
97+
*/
98+
void insertPage( QgsLayoutItemPage *page SIP_TRANSFER, int beforePage );
99+
100+
/**
101+
* Deletes a page from the collection. The page will automatically be removed
102+
* from the collection's layout().
103+
*
104+
* Page numbers in collections begin at 0 - so a \a pageNumber of 0 will delete
105+
* the first page in the collection.
106+
*/
107+
void deletePage( int pageNumber );
108+
51109
/**
52110
* Sets the \a symbol to use for drawing pages in the collection.
53111
*
@@ -69,6 +127,8 @@ class CORE_EXPORT QgsLayoutPageCollection : public QObject
69127
//! Symbol for drawing pages
70128
std::unique_ptr< QgsFillSymbol > mPageStyleSymbol;
71129

130+
QList< QgsLayoutItemPage * > mPages;
131+
72132
void createDefaultPageStyleSymbol();
73133
};
74134

tests/src/python/test_qgslayoutpagecollection.py

Lines changed: 73 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,10 @@
1313
__revision__ = '$Format:%H$'
1414

1515
import qgis # NOQA
16+
import sip
1617

17-
from qgis.core import QgsUnitTypes, QgsLayout, QgsProject, QgsLayoutPageCollection, QgsSimpleFillSymbolLayer, QgsFillSymbol
18-
from qgis.PyQt.QtCore import Qt
18+
from qgis.core import QgsUnitTypes, QgsLayout, QgsLayoutItemPage, QgsProject, QgsLayoutPageCollection, QgsSimpleFillSymbolLayer, QgsFillSymbol
19+
from qgis.PyQt.QtCore import Qt, QCoreApplication, QEvent
1920
from qgis.testing import start_app, unittest
2021

2122
start_app()
@@ -49,6 +50,76 @@ def testSymbol(self):
4950
self.assertEqual(collection.pageStyleSymbol().symbolLayer(0).color().name(), '#00ff00')
5051
self.assertEqual(collection.pageStyleSymbol().symbolLayer(0).strokeColor().name(), '#ff0000')
5152

53+
def testPages(self):
54+
"""
55+
Test adding/retrieving/deleting pages from the collection
56+
"""
57+
p = QgsProject()
58+
l = QgsLayout(p)
59+
collection = l.pageCollection()
60+
61+
self.assertEqual(collection.pageCount(), 0)
62+
self.assertFalse(collection.pages())
63+
self.assertFalse(collection.page(-1))
64+
self.assertFalse(collection.page(0))
65+
self.assertFalse(collection.page(1))
66+
67+
# add a page
68+
page = QgsLayoutItemPage(l)
69+
page.setPageSize('A4')
70+
collection.addPage(page)
71+
72+
self.assertTrue(page in l.items())
73+
74+
self.assertEqual(collection.pageCount(), 1)
75+
self.assertEqual(collection.pages(), [page])
76+
self.assertFalse(collection.page(-1))
77+
self.assertEqual(collection.page(0), page)
78+
self.assertFalse(collection.page(1))
79+
80+
# add a second page
81+
page2 = QgsLayoutItemPage(l)
82+
page2.setPageSize('A5')
83+
collection.addPage(page2)
84+
85+
self.assertEqual(collection.pageCount(), 2)
86+
self.assertEqual(collection.pages(), [page, page2])
87+
self.assertFalse(collection.page(-1))
88+
self.assertEqual(collection.page(0), page)
89+
self.assertEqual(collection.page(1), page2)
90+
91+
# insert a page
92+
page3 = QgsLayoutItemPage(l)
93+
page3.setPageSize('A3')
94+
collection.insertPage(page3, 1)
95+
self.assertTrue(page3 in l.items())
96+
97+
self.assertEqual(collection.pageCount(), 3)
98+
self.assertEqual(collection.pages(), [page, page3, page2])
99+
self.assertEqual(collection.page(0), page)
100+
self.assertEqual(collection.page(1), page3)
101+
self.assertEqual(collection.page(2), page2)
102+
103+
# delete page
104+
collection.deletePage(-1)
105+
self.assertEqual(collection.pageCount(), 3)
106+
self.assertEqual(collection.pages(), [page, page3, page2])
107+
collection.deletePage(100)
108+
self.assertEqual(collection.pageCount(), 3)
109+
self.assertEqual(collection.pages(), [page, page3, page2])
110+
collection.deletePage(1)
111+
self.assertEqual(collection.pageCount(), 2)
112+
self.assertEqual(collection.pages(), [page, page2])
113+
114+
# make sure page was deleted
115+
QCoreApplication.sendPostedEvents(None, QEvent.DeferredDelete)
116+
self.assertTrue(sip.isdeleted(page3))
117+
118+
del l
119+
QCoreApplication.sendPostedEvents(None, QEvent.DeferredDelete)
120+
self.assertTrue(sip.isdeleted(page))
121+
self.assertTrue(sip.isdeleted(page2))
122+
52123

53124
if __name__ == '__main__':
54125
unittest.main()

0 commit comments

Comments
 (0)