Skip to content

Commit

Permalink
Add support for adding/removing pages to a collection
Browse files Browse the repository at this point in the history
  • Loading branch information
nyalldawson committed Jul 25, 2017
1 parent ea32391 commit 79a4694
Show file tree
Hide file tree
Showing 6 changed files with 289 additions and 2 deletions.
18 changes: 18 additions & 0 deletions python/core/layout/qgslayoutitem.sip
Expand Up @@ -18,6 +18,24 @@ class QgsLayoutItem : QgsLayoutObject, QGraphicsRectItem

%TypeHeaderCode
#include "qgslayoutitem.h"

#include <qgslayoutitemshape.h>
#include <qgslayoutitempage.h>
%End

%ConvertToSubClassCode
// the conversions have to be static, because they're using multiple inheritance
// (seen in PyQt4 .sip files for some QGraphicsItem classes)
switch ( sipCpp->type() )
{
// really, these *should* use the constants from QgsLayoutItemRegistry, but sip doesn't like that!
case QGraphicsItem::UserType + 101:
sipType = sipType_QgsLayoutItemPage;
*sipCppRet = static_cast<QgsLayoutItemPage *>( sipCpp );
break;
default:
sipType = 0;
}
%End
public:

Expand Down
60 changes: 60 additions & 0 deletions python/core/layout/qgslayoutpagecollection.sip
Expand Up @@ -26,12 +26,72 @@ class QgsLayoutPageCollection : QObject
Constructor for QgsLayoutItemPage, with the specified parent ``layout``.
%End

~QgsLayoutPageCollection();

QgsLayout *layout() const;
%Docstring
Returns the layout this collection belongs to.
:rtype: QgsLayout
%End

QList< QgsLayoutItemPage * > pages();
%Docstring
Returns a list of pages in the collection.
.. seealso:: page()
.. seealso:: pageCount()
:rtype: list of QgsLayoutItemPage
%End

int pageCount() const;
%Docstring
Returns the number of pages in the collection.
.. seealso:: pages()
:rtype: int
%End

QgsLayoutItemPage *page( int pageNumber );
%Docstring
Returns a specific page (by ``pageNumber``) from the collection.
Internal page numbering starts at 0 - so a ``pageNumber`` of 0
corresponds to the first page in the collection.
A None is returned if an invalid page number is specified.
.. seealso:: pages()
:rtype: QgsLayoutItemPage
%End

void addPage( QgsLayoutItemPage *page /Transfer/ );
%Docstring
Adds a ``page`` to the collection. Ownership of the ``page`` is transferred
to the collection, and the page will automatically be added to the collection's
layout() (there is no need to manually add the page item to the layout).
The page will be added after all pages currently contained in the collection.
.. seealso:: insertPage()
%End

void insertPage( QgsLayoutItemPage *page /Transfer/, int beforePage );
%Docstring
Inserts a ``page`` into a specific position in the collection.

Ownership of the ``page`` is transferred
to the collection, and the page will automatically be added to the collection's
layout() (there is no need to manually add the page item to the layout).

The page will be added after before the page number specified by ``beforePage``.
(Page numbers in collections begin at 0 - so a ``beforePage`` of 0 will insert
the page before all existing pages).

.. seealso:: addPage()
%End

void deletePage( int pageNumber );
%Docstring
Deletes a page from the collection. The page will automatically be removed
from the collection's layout().

Page numbers in collections begin at 0 - so a ``pageNumber`` of 0 will delete
the first page in the collection.
%End

void setPageStyleSymbol( QgsFillSymbol *symbol );
%Docstring
Sets the ``symbol`` to use for drawing pages in the collection.
Expand Down
22 changes: 22 additions & 0 deletions src/core/layout/qgslayoutitem.h
Expand Up @@ -35,6 +35,28 @@ class QPainter;
*/
class CORE_EXPORT QgsLayoutItem : public QgsLayoutObject, public QGraphicsRectItem
{
#ifdef SIP_RUN
#include <qgslayoutitemshape.h>
#include <qgslayoutitempage.h>
#endif


#ifdef SIP_RUN
SIP_CONVERT_TO_SUBCLASS_CODE
// the conversions have to be static, because they're using multiple inheritance
// (seen in PyQt4 .sip files for some QGraphicsItem classes)
switch ( sipCpp->type() )
{
// really, these *should* use the constants from QgsLayoutItemRegistry, but sip doesn't like that!
case QGraphicsItem::UserType + 101:
sipType = sipType_QgsLayoutItemPage;
*sipCppRet = static_cast<QgsLayoutItemPage *>( sipCpp );
break;
default:
sipType = 0;
}
SIP_END
#endif

Q_OBJECT

Expand Down
56 changes: 56 additions & 0 deletions src/core/layout/qgslayoutpagecollection.cpp
Expand Up @@ -24,6 +24,15 @@ QgsLayoutPageCollection::QgsLayoutPageCollection( QgsLayout *layout )
createDefaultPageStyleSymbol();
}

QgsLayoutPageCollection::~QgsLayoutPageCollection()
{
Q_FOREACH ( QgsLayoutItemPage *page, mPages )
{
mLayout->removeItem( page );
page->deleteLater();
}
}

void QgsLayoutPageCollection::setPageStyleSymbol( QgsFillSymbol *symbol )
{
if ( !symbol )
Expand All @@ -37,6 +46,53 @@ QgsLayout *QgsLayoutPageCollection::layout() const
return mLayout;
}

QList<QgsLayoutItemPage *> QgsLayoutPageCollection::pages()
{
return mPages;
}

int QgsLayoutPageCollection::pageCount() const
{
return mPages.count();
}

QgsLayoutItemPage *QgsLayoutPageCollection::page( int pageNumber )
{
return mPages.value( pageNumber );
}

void QgsLayoutPageCollection::addPage( QgsLayoutItemPage *page )
{
mPages.append( page );
mLayout->addItem( page );
}

void QgsLayoutPageCollection::insertPage( QgsLayoutItemPage *page, int beforePage )
{
if ( beforePage < 0 )
beforePage = 0;

if ( beforePage >= mPages.count() )
{
mPages.append( page );
}
else
{
mPages.insert( beforePage, page );
}
mLayout->addItem( page );
}

void QgsLayoutPageCollection::deletePage( int pageNumber )
{
if ( pageNumber < 0 || pageNumber >= mPages.count() )
return;

QgsLayoutItemPage *page = mPages.takeAt( pageNumber );
mLayout->removeItem( page );
page->deleteLater();
}

void QgsLayoutPageCollection::createDefaultPageStyleSymbol()
{
QgsStringMap properties;
Expand Down
60 changes: 60 additions & 0 deletions src/core/layout/qgslayoutpagecollection.h
Expand Up @@ -20,6 +20,7 @@
#include "qgis_core.h"
#include "qgis_sip.h"
#include "qgssymbol.h"
#include "qgslayoutitempage.h"
#include <QObject>
#include <memory>

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

~QgsLayoutPageCollection();

/**
* Returns the layout this collection belongs to.
*/
QgsLayout *layout() const;

/**
* Returns a list of pages in the collection.
* \see page()
* \see pageCount()
*/
QList< QgsLayoutItemPage * > pages();

/**
* Returns the number of pages in the collection.
* \see pages()
*/
int pageCount() const;

/**
* Returns a specific page (by \a pageNumber) from the collection.
* Internal page numbering starts at 0 - so a \a pageNumber of 0
* corresponds to the first page in the collection.
* A nullptr is returned if an invalid page number is specified.
* \see pages()
*/
QgsLayoutItemPage *page( int pageNumber );

/**
* Adds a \a page to the collection. Ownership of the \a page is transferred
* to the collection, and the page will automatically be added to the collection's
* layout() (there is no need to manually add the page item to the layout).
* The page will be added after all pages currently contained in the collection.
* \see insertPage()
*/
void addPage( QgsLayoutItemPage *page SIP_TRANSFER );

/**
* Inserts a \a page into a specific position in the collection.
*
* Ownership of the \a page is transferred
* to the collection, and the page will automatically be added to the collection's
* layout() (there is no need to manually add the page item to the layout).
*
* The page will be added after before the page number specified by \a beforePage.
* (Page numbers in collections begin at 0 - so a \a beforePage of 0 will insert
* the page before all existing pages).
*
* \see addPage()
*/
void insertPage( QgsLayoutItemPage *page SIP_TRANSFER, int beforePage );

/**
* Deletes a page from the collection. The page will automatically be removed
* from the collection's layout().
*
* Page numbers in collections begin at 0 - so a \a pageNumber of 0 will delete
* the first page in the collection.
*/
void deletePage( int pageNumber );

/**
* Sets the \a symbol to use for drawing pages in the collection.
*
Expand All @@ -69,6 +127,8 @@ class CORE_EXPORT QgsLayoutPageCollection : public QObject
//! Symbol for drawing pages
std::unique_ptr< QgsFillSymbol > mPageStyleSymbol;

QList< QgsLayoutItemPage * > mPages;

void createDefaultPageStyleSymbol();
};

Expand Down
75 changes: 73 additions & 2 deletions tests/src/python/test_qgslayoutpagecollection.py
Expand Up @@ -13,9 +13,10 @@
__revision__ = '$Format:%H$'

import qgis # NOQA
import sip

from qgis.core import QgsUnitTypes, QgsLayout, QgsProject, QgsLayoutPageCollection, QgsSimpleFillSymbolLayer, QgsFillSymbol
from qgis.PyQt.QtCore import Qt
from qgis.core import QgsUnitTypes, QgsLayout, QgsLayoutItemPage, QgsProject, QgsLayoutPageCollection, QgsSimpleFillSymbolLayer, QgsFillSymbol
from qgis.PyQt.QtCore import Qt, QCoreApplication, QEvent
from qgis.testing import start_app, unittest

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

def testPages(self):
"""
Test adding/retrieving/deleting pages from the collection
"""
p = QgsProject()
l = QgsLayout(p)
collection = l.pageCollection()

self.assertEqual(collection.pageCount(), 0)
self.assertFalse(collection.pages())
self.assertFalse(collection.page(-1))
self.assertFalse(collection.page(0))
self.assertFalse(collection.page(1))

# add a page
page = QgsLayoutItemPage(l)
page.setPageSize('A4')
collection.addPage(page)

self.assertTrue(page in l.items())

self.assertEqual(collection.pageCount(), 1)
self.assertEqual(collection.pages(), [page])
self.assertFalse(collection.page(-1))
self.assertEqual(collection.page(0), page)
self.assertFalse(collection.page(1))

# add a second page
page2 = QgsLayoutItemPage(l)
page2.setPageSize('A5')
collection.addPage(page2)

self.assertEqual(collection.pageCount(), 2)
self.assertEqual(collection.pages(), [page, page2])
self.assertFalse(collection.page(-1))
self.assertEqual(collection.page(0), page)
self.assertEqual(collection.page(1), page2)

# insert a page
page3 = QgsLayoutItemPage(l)
page3.setPageSize('A3')
collection.insertPage(page3, 1)
self.assertTrue(page3 in l.items())

self.assertEqual(collection.pageCount(), 3)
self.assertEqual(collection.pages(), [page, page3, page2])
self.assertEqual(collection.page(0), page)
self.assertEqual(collection.page(1), page3)
self.assertEqual(collection.page(2), page2)

# delete page
collection.deletePage(-1)
self.assertEqual(collection.pageCount(), 3)
self.assertEqual(collection.pages(), [page, page3, page2])
collection.deletePage(100)
self.assertEqual(collection.pageCount(), 3)
self.assertEqual(collection.pages(), [page, page3, page2])
collection.deletePage(1)
self.assertEqual(collection.pageCount(), 2)
self.assertEqual(collection.pages(), [page, page2])

# make sure page was deleted
QCoreApplication.sendPostedEvents(None, QEvent.DeferredDelete)
self.assertTrue(sip.isdeleted(page3))

del l
QCoreApplication.sendPostedEvents(None, QEvent.DeferredDelete)
self.assertTrue(sip.isdeleted(page))
self.assertTrue(sip.isdeleted(page2))


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

0 comments on commit 79a4694

Please sign in to comment.