From 09dd6db97b5b19979669677e53baf675d976d62f Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Tue, 18 Jul 2017 14:53:41 +1000 Subject: [PATCH] Add API to save/restore QgsLayoutObject properties to XML --- python/core/layout/qgslayoutobject.sip | 20 ++++ src/core/layout/qgslayoutobject.cpp | 48 ++++++++++ src/core/layout/qgslayoutobject.h | 19 ++++ tests/src/core/testqgslayoutobject.cpp | 126 +++++++++++++++++++++++++ 4 files changed, 213 insertions(+) diff --git a/python/core/layout/qgslayoutobject.sip b/python/core/layout/qgslayoutobject.sip index e60d5673ddf..cfe1fd3de18 100644 --- a/python/core/layout/qgslayoutobject.sip +++ b/python/core/layout/qgslayoutobject.sip @@ -168,6 +168,26 @@ class QgsLayoutObject: QObject, QgsExpressionContextGenerator protected: + bool writeObjectPropertiesToElement( QDomElement &parentElement, QDomDocument &document, const QgsReadWriteContext &context ) const; +%Docstring + Stores object properties within an XML DOM element. + \param parentElement is the parent DOM element to store the object's properties in + \param document DOM document + :return: true if write was successful +.. seealso:: readObjectPropertiesFromElement() + :rtype: bool +%End + + bool readObjectPropertiesFromElement( const QDomElement &parentElement, const QDomDocument &document, const QgsReadWriteContext &context ); +%Docstring + Sets object properties from a DOM element + \param parentElement is the parent DOM element for the object + \param document DOM document + :return: true if read was successful +.. seealso:: writeObjectPropertiesToElement() + :rtype: bool +%End + diff --git a/src/core/layout/qgslayoutobject.cpp b/src/core/layout/qgslayoutobject.cpp index d13163771c0..4c4462794c9 100644 --- a/src/core/layout/qgslayoutobject.cpp +++ b/src/core/layout/qgslayoutobject.cpp @@ -122,3 +122,51 @@ QgsExpressionContext QgsLayoutObject::createExpressionContext() const return QgsExpressionContext() << QgsExpressionContextUtils::globalScope(); } } + +bool QgsLayoutObject::writeObjectPropertiesToElement( QDomElement &parentElement, QDomDocument &document, const QgsReadWriteContext & ) const +{ + if ( parentElement.isNull() ) + { + return false; + } + + //create object element + QDomElement objectElement = document.createElement( "LayoutObject" ); + + QDomElement ddPropsElement = document.createElement( QStringLiteral( "dataDefinedProperties" ) ); + mDataDefinedProperties.writeXml( ddPropsElement, sPropertyDefinitions ); + objectElement.appendChild( ddPropsElement ); + + //custom properties + mCustomProperties.writeXml( objectElement, document ); + + parentElement.appendChild( objectElement ); + return true; +} + +bool QgsLayoutObject::readObjectPropertiesFromElement( const QDomElement &parentElement, const QDomDocument &document, const QgsReadWriteContext & ) +{ + Q_UNUSED( document ); + if ( parentElement.isNull() ) + { + return false; + } + + QDomNodeList objectNodeList = parentElement.elementsByTagName( "LayoutObject" ); + if ( objectNodeList.size() < 1 ) + { + return false; + } + QDomElement objectElement = objectNodeList.at( 0 ).toElement(); + + QDomNode propsNode = objectElement.namedItem( QStringLiteral( "dataDefinedProperties" ) ); + if ( !propsNode.isNull() ) + { + mDataDefinedProperties.readXml( propsNode.toElement(), sPropertyDefinitions ); + } + + //custom properties + mCustomProperties.readXml( objectElement ); + + return true; +} diff --git a/src/core/layout/qgslayoutobject.h b/src/core/layout/qgslayoutobject.h index 4064d216722..a9ae4a8cc3d 100644 --- a/src/core/layout/qgslayoutobject.h +++ b/src/core/layout/qgslayoutobject.h @@ -28,6 +28,7 @@ class QgsLayout; class QPainter; +class QgsReadWriteContext; /** * \ingroup core @@ -188,6 +189,24 @@ class CORE_EXPORT QgsLayoutObject: public QObject, public QgsExpressionContextGe protected: + /** + * Stores object properties within an XML DOM element. + * \param parentElement is the parent DOM element to store the object's properties in + * \param document DOM document + * \returns true if write was successful + * \see readObjectPropertiesFromElement() + */ + bool writeObjectPropertiesToElement( QDomElement &parentElement, QDomDocument &document, const QgsReadWriteContext &context ) const; + + /** + * Sets object properties from a DOM element + * \param parentElement is the parent DOM element for the object + * \param document DOM document + * \returns true if read was successful + * \see writeObjectPropertiesToElement() + */ + bool readObjectPropertiesFromElement( const QDomElement &parentElement, const QDomDocument &document, const QgsReadWriteContext &context ); + QgsLayout *mLayout = nullptr; QgsPropertyCollection mDataDefinedProperties; diff --git a/tests/src/core/testqgslayoutobject.cpp b/tests/src/core/testqgslayoutobject.cpp index ff0ec46836b..63d5c429c58 100644 --- a/tests/src/core/testqgslayoutobject.cpp +++ b/tests/src/core/testqgslayoutobject.cpp @@ -19,6 +19,7 @@ #include "qgslayout.h" #include "qgstest.h" #include "qgsproject.h" +#include "qgsreadwritecontext.h" class TestQgsLayoutObject: public QObject { @@ -33,6 +34,10 @@ class TestQgsLayoutObject: public QObject void layout(); //test fetching layout from QgsLayoutObject void customProperties(); void context(); + void writeReadXml(); + void writeRetrieveDDProperty(); //test writing and retrieving dd properties from xml + void writeRetrieveCustomProperties(); //test writing/retreiving custom properties from xml + private: QString mReport; @@ -138,6 +143,127 @@ void TestQgsLayoutObject::context() delete object; } +void TestQgsLayoutObject::writeReadXml() +{ + QgsProject p; + QgsLayout l( &p ); + + QgsLayoutObject *object = new QgsLayoutObject( &l ); + QDomImplementation DomImplementation; + QDomDocumentType documentType = + DomImplementation.createDocumentType( + "qgis", "http://mrcc.com/qgis.dtd", "SYSTEM" ); + QDomDocument doc( documentType ); + + //test writing with no parent node + QDomElement rootNode = doc.createElement( "qgis" ); + QDomElement noNode; + QCOMPARE( object->writeObjectPropertiesToElement( noNode, doc, QgsReadWriteContext() ), false ); + + //test writing with node + QDomElement layoutObjectElem = doc.createElement( "item" ); + rootNode.appendChild( layoutObjectElem ); + QVERIFY( object->writeObjectPropertiesToElement( layoutObjectElem, doc, QgsReadWriteContext() ) ); + + //check if object node was written + QDomNodeList evalNodeList = rootNode.elementsByTagName( "LayoutObject" ); + QCOMPARE( evalNodeList.count(), 1 ); + + //test reading node + QgsLayoutObject *readObject = new QgsLayoutObject( &l ); + + //test reading with no node + QCOMPARE( readObject->readObjectPropertiesFromElement( noNode, doc, QgsReadWriteContext() ), false ); + + //test node with no layout object child + QDomElement badLayoutObjectElem = doc.createElement( "item" ); + rootNode.appendChild( badLayoutObjectElem ); + QCOMPARE( readObject->readObjectPropertiesFromElement( badLayoutObjectElem, doc, QgsReadWriteContext() ), false ); + + //test reading node + QVERIFY( readObject->readObjectPropertiesFromElement( layoutObjectElem, doc, QgsReadWriteContext() ) ); + + delete object; + delete readObject; +} + +void TestQgsLayoutObject::writeRetrieveDDProperty() +{ + QgsProject p; + QgsLayout l( &p ); + + QgsLayoutObject *object = new QgsLayoutObject( &l ); + object->dataDefinedProperties().setProperty( QgsLayoutObject::TestProperty, QgsProperty::fromExpression( QStringLiteral( "10 + 40" ) ) ); + + //test writing object with dd settings + QDomImplementation DomImplementation; + QDomDocumentType documentType = + DomImplementation.createDocumentType( + QStringLiteral( "qgis" ), QStringLiteral( "http://mrcc.com/qgis.dtd" ), QStringLiteral( "SYSTEM" ) ); + QDomDocument doc( documentType ); + QDomElement rootNode = doc.createElement( QStringLiteral( "qgis" ) ); + QVERIFY( object->writeObjectPropertiesToElement( rootNode, doc, QgsReadWriteContext() ) ); + + //check if object node was written + QDomNodeList evalNodeList = rootNode.elementsByTagName( QStringLiteral( "LayoutObject" ) ); + QCOMPARE( evalNodeList.count(), 1 ); + + //test reading node containing dd settings + QgsLayoutObject *readObject = new QgsLayoutObject( &l ); + QVERIFY( readObject->readObjectPropertiesFromElement( rootNode, doc, QgsReadWriteContext() ) ); + + //test getting not set dd from restored object + QgsProperty dd = readObject->dataDefinedProperties().property( QgsLayoutObject::BlendMode ); + QVERIFY( !dd ); + + //test getting good property + dd = readObject->dataDefinedProperties().property( QgsLayoutObject::TestProperty ); + QVERIFY( dd ); + QVERIFY( dd.isActive() ); + QCOMPARE( dd.propertyType(), QgsProperty::ExpressionBasedProperty ); + + delete object; + delete readObject; +} + +void TestQgsLayoutObject::writeRetrieveCustomProperties() +{ + QgsProject p; + QgsLayout l( &p ); + + QgsLayoutObject *object = new QgsLayoutObject( &l ); + + object->setCustomProperty( QStringLiteral( "testprop" ), "testval" ); + object->setCustomProperty( QStringLiteral( "testprop2" ), 5 ); + + //test writing object with custom properties + QDomImplementation DomImplementation; + QDomDocumentType documentType = + DomImplementation.createDocumentType( + QStringLiteral( "qgis" ), QStringLiteral( "http://mrcc.com/qgis.dtd" ), QStringLiteral( "SYSTEM" ) ); + QDomDocument doc( documentType ); + QDomElement rootNode = doc.createElement( QStringLiteral( "qgis" ) ); + QVERIFY( object->writeObjectPropertiesToElement( rootNode, doc, QgsReadWriteContext() ) ); + + //check if object node was written + QDomNodeList evalNodeList = rootNode.elementsByTagName( QStringLiteral( "LayoutObject" ) ); + QCOMPARE( evalNodeList.count(), 1 ); + + //test reading node containing custom properties + QgsLayoutObject *readObject = new QgsLayoutObject( &l ); + QVERIFY( readObject->readObjectPropertiesFromElement( rootNode, doc, QgsReadWriteContext() ) ); + + //test retrieved custom properties + QCOMPARE( readObject->customProperties().length(), 2 ); + QVERIFY( readObject->customProperties().contains( QString( "testprop" ) ) ); + QVERIFY( readObject->customProperties().contains( QString( "testprop2" ) ) ); + QCOMPARE( readObject->customProperty( "testprop" ).toString(), QString( "testval" ) ); + QCOMPARE( readObject->customProperty( "testprop2" ).toInt(), 5 ); + + delete object; + delete readObject; +} + QGSTEST_MAIN( TestQgsLayoutObject ) #include "testqgslayoutobject.moc"