From d8af098d837d4dabaf0d4ea4c52121cd16666eca Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Fri, 29 Dec 2017 16:03:52 +1000 Subject: [PATCH] Code shuffle and cleanup --- python/core/core_auto.sip | 3 + .../core/layout/qgsabstractreportsection.sip | 182 ++---------------- python/core/layout/qgsreport.sip | 59 ++++++ .../layout/qgsreportsectionfieldgroup.sip | 99 ++++++++++ python/core/layout/qgsreportsectionlayout.sip | 67 +++++++ src/core/CMakeLists.txt | 6 + src/core/layout/qgsabstractreportsection.cpp | 171 +--------------- src/core/layout/qgsabstractreportsection.h | 173 ++--------------- src/core/layout/qgsreport.cpp | 35 ++++ src/core/layout/qgsreport.h | 68 +++++++ .../layout/qgsreportsectionfieldgroup.cpp | 147 ++++++++++++++ src/core/layout/qgsreportsectionfieldgroup.h | 109 +++++++++++ src/core/layout/qgsreportsectionlayout.cpp | 63 ++++++ src/core/layout/qgsreportsectionlayout.h | 70 +++++++ 14 files changed, 754 insertions(+), 498 deletions(-) create mode 100644 python/core/layout/qgsreport.sip create mode 100644 python/core/layout/qgsreportsectionfieldgroup.sip create mode 100644 python/core/layout/qgsreportsectionlayout.sip create mode 100644 src/core/layout/qgsreport.cpp create mode 100644 src/core/layout/qgsreport.h create mode 100644 src/core/layout/qgsreportsectionfieldgroup.cpp create mode 100644 src/core/layout/qgsreportsectionfieldgroup.h create mode 100644 src/core/layout/qgsreportsectionlayout.cpp create mode 100644 src/core/layout/qgsreportsectionlayout.h diff --git a/python/core/core_auto.sip b/python/core/core_auto.sip index 1342a112ef0..8005fc0ec92 100644 --- a/python/core/core_auto.sip +++ b/python/core/core_auto.sip @@ -175,6 +175,9 @@ %Include layout/qgslayoutsnapper.sip %Include layout/qgslayoutundocommand.sip %Include layout/qgslayoututils.sip +%Include layout/qgsreport.sip +%Include layout/qgsreportsectionfieldgroup.sip +%Include layout/qgsreportsectionlayout.sip %Include metadata/qgslayermetadata.sip %Include metadata/qgslayermetadatavalidator.sip %Include metadata/qgslayermetadataformatter.sip diff --git a/python/core/layout/qgsabstractreportsection.sip b/python/core/layout/qgsabstractreportsection.sip index 4b2fe599f11..34afcbbef2d 100644 --- a/python/core/layout/qgsabstractreportsection.sip +++ b/python/core/layout/qgsabstractreportsection.sip @@ -10,8 +10,18 @@ -class QgsReportContext +class QgsReportSectionContext { +%Docstring + Current context for a report section. + +.. warning:: + + This is not considered stable API, and may change in future QGIS releases. It is +exposed to the Python bindings for unit testing purposes only. + +.. versionadded:: 3.0 +%End %TypeHeaderCode #include "qgsabstractreportsection.h" @@ -250,14 +260,14 @@ Removes the child section at the specified ``index``, deleting it. .. seealso:: :py:func:`children()` %End - void setContext( const QgsReportContext &context ); + void setContext( const QgsReportSectionContext &context ); %Docstring Sets the current ``context`` for this section. .. seealso:: :py:func:`context()` %End - const QgsReportContext &context() const; + const QgsReportSectionContext &context() const; %Docstring Returns the current context for this section. @@ -290,172 +300,6 @@ Sets the ``parent`` report section. QgsAbstractReportSection( const QgsAbstractReportSection &other ); }; -class QgsReportSectionLayout : QgsAbstractReportSection -{ -%Docstring - A report section consisting of a single QgsLayout body. - -.. warning:: - - This is not considered stable API, and may change in future QGIS releases. It is -exposed to the Python bindings for unit testing purposes only. - -.. versionadded:: 3.0 -%End - -%TypeHeaderCode -#include "qgsabstractreportsection.h" -%End - public: - - QgsReportSectionLayout( QgsAbstractReportSection *parent = 0 ); -%Docstring -Constructor for QgsReportSectionLayout, attached to the specified ``parent`` section. -Note that ownership is not transferred to ``parent``. -%End - - QgsLayout *body(); -%Docstring -Returns the body layout for the section. - -.. seealso:: :py:func:`setBody()` -%End - - void setBody( QgsLayout *body /Transfer/ ); -%Docstring -Sets the ``body`` layout for the section. Ownership of ``body`` -is transferred to the report section. - -.. seealso:: :py:func:`body()` -%End - - virtual QgsReportSectionLayout *clone() const /Factory/; - - virtual bool beginRender(); - - virtual QgsLayout *nextBody( bool &ok ); - - -}; - -class QgsReportSectionFieldGroup : QgsAbstractReportSection -{ -%Docstring - A report section consisting of a features - -.. warning:: - - This is not considered stable API, and may change in future QGIS releases. It is -exposed to the Python bindings for unit testing purposes only. - -.. versionadded:: 3.0 -%End - -%TypeHeaderCode -#include "qgsabstractreportsection.h" -%End - public: - - QgsReportSectionFieldGroup( QgsAbstractReportSection *parent = 0 ); -%Docstring -Constructor for QgsReportSectionFieldGroup, attached to the specified ``parent`` section. -Note that ownership is not transferred to ``parent``. -%End - - QgsLayout *body(); -%Docstring -Returns the body layout for the section. - -.. seealso:: :py:func:`setBody()` -%End - - void setBody( QgsLayout *body /Transfer/ ); -%Docstring -Sets the ``body`` layout for the section. Ownership of ``body`` -is transferred to the report section. - -.. seealso:: :py:func:`body()` -%End - - QgsVectorLayer *layer(); -%Docstring -Returns the vector layer associated with this section. - -.. seealso:: :py:func:`setLayer()` -%End - - void setLayer( QgsVectorLayer *layer ); -%Docstring -Sets the vector ``layer`` associated with this section. - -.. seealso:: :py:func:`layer()` -%End - - QString field() const; -%Docstring -Returns the field associated with this section. - -.. seealso:: :py:func:`setField()` -%End - - void setField( const QString &field ); -%Docstring -Sets the ``field`` associated with this section. - -.. seealso:: :py:func:`field()` -%End - - virtual QgsReportSectionFieldGroup *clone() const /Factory/; - - virtual bool beginRender(); - - virtual QgsLayout *nextBody( bool &ok ); - - virtual void reset(); - - -}; - - -class QgsReport : QgsAbstractReportSection -{ -%Docstring - Represents a report for use with the QgsLayout engine. - -Reports consist of multiple sections, represented by :py:class:`QgsAbstractReportSection` -subclasses. - -.. warning:: - - This is not considered stable API, and may change in future QGIS releases. It is -exposed to the Python bindings for unit testing purposes only. - -.. versionadded:: 3.0 -%End - -%TypeHeaderCode -#include "qgsabstractreportsection.h" -%End - public: - - QgsReport( QgsProject *project ); -%Docstring -Constructor for QgsReport, associated with the specified -``project``. - -Note that ownership is not transferred to ``project``. -%End - - QgsProject *project(); -%Docstring -Returns the associated project. -%End - - virtual QgsReport *clone() const /Factory/; - - -}; - /************************************************************************ * This file has been generated automatically from * diff --git a/python/core/layout/qgsreport.sip b/python/core/layout/qgsreport.sip new file mode 100644 index 00000000000..624bbf5c3d3 --- /dev/null +++ b/python/core/layout/qgsreport.sip @@ -0,0 +1,59 @@ +/************************************************************************ + * This file has been generated automatically from * + * * + * src/core/layout/qgsreport.h * + * * + * Do not edit manually ! Edit header and run scripts/sipify.pl again * + ************************************************************************/ + + + + + +class QgsReport : QgsAbstractReportSection +{ +%Docstring + Represents a report for use with the QgsLayout engine. + +Reports consist of multiple sections, represented by QgsAbstractReportSection +subclasses. + +.. warning:: + + This is not considered stable API, and may change in future QGIS releases. It is +exposed to the Python bindings for unit testing purposes only. + +.. versionadded:: 3.0 +%End + +%TypeHeaderCode +#include "qgsreport.h" +%End + public: + + QgsReport( QgsProject *project ); +%Docstring +Constructor for QgsReport, associated with the specified +``project``. + +Note that ownership is not transferred to ``project``. +%End + + QgsProject *project(); +%Docstring +Returns the associated project. +%End + + virtual QgsReport *clone() const /Factory/; + + +}; + + +/************************************************************************ + * This file has been generated automatically from * + * * + * src/core/layout/qgsreport.h * + * * + * Do not edit manually ! Edit header and run scripts/sipify.pl again * + ************************************************************************/ diff --git a/python/core/layout/qgsreportsectionfieldgroup.sip b/python/core/layout/qgsreportsectionfieldgroup.sip new file mode 100644 index 00000000000..977e65f95fe --- /dev/null +++ b/python/core/layout/qgsreportsectionfieldgroup.sip @@ -0,0 +1,99 @@ +/************************************************************************ + * This file has been generated automatically from * + * * + * src/core/layout/qgsreportsectionfieldgroup.h * + * * + * Do not edit manually ! Edit header and run scripts/sipify.pl again * + ************************************************************************/ + + + + + +class QgsReportSectionFieldGroup : QgsAbstractReportSection +{ +%Docstring + A report section consisting of a features + +.. warning:: + + This is not considered stable API, and may change in future QGIS releases. It is +exposed to the Python bindings for unit testing purposes only. + +.. versionadded:: 3.0 +%End + +%TypeHeaderCode +#include "qgsreportsectionfieldgroup.h" +%End + public: + + QgsReportSectionFieldGroup( QgsAbstractReportSection *parent = 0 ); +%Docstring +Constructor for QgsReportSectionFieldGroup, attached to the specified ``parent`` section. +Note that ownership is not transferred to ``parent``. +%End + + QgsLayout *body(); +%Docstring +Returns the body layout for the section. + +.. seealso:: :py:func:`setBody()` +%End + + void setBody( QgsLayout *body /Transfer/ ); +%Docstring +Sets the ``body`` layout for the section. Ownership of ``body`` +is transferred to the report section. + +.. seealso:: :py:func:`body()` +%End + + QgsVectorLayer *layer(); +%Docstring +Returns the vector layer associated with this section. + +.. seealso:: :py:func:`setLayer()` +%End + + void setLayer( QgsVectorLayer *layer ); +%Docstring +Sets the vector ``layer`` associated with this section. + +.. seealso:: :py:func:`layer()` +%End + + QString field() const; +%Docstring +Returns the field associated with this section. + +.. seealso:: :py:func:`setField()` +%End + + void setField( const QString &field ); +%Docstring +Sets the ``field`` associated with this section. + +.. seealso:: :py:func:`field()` +%End + + virtual QgsReportSectionFieldGroup *clone() const /Factory/; + + virtual bool beginRender(); + + virtual QgsLayout *nextBody( bool &ok ); + + virtual void reset(); + + +}; + + + +/************************************************************************ + * This file has been generated automatically from * + * * + * src/core/layout/qgsreportsectionfieldgroup.h * + * * + * Do not edit manually ! Edit header and run scripts/sipify.pl again * + ************************************************************************/ diff --git a/python/core/layout/qgsreportsectionlayout.sip b/python/core/layout/qgsreportsectionlayout.sip new file mode 100644 index 00000000000..7bfdab56365 --- /dev/null +++ b/python/core/layout/qgsreportsectionlayout.sip @@ -0,0 +1,67 @@ +/************************************************************************ + * This file has been generated automatically from * + * * + * src/core/layout/qgsreportsectionlayout.h * + * * + * Do not edit manually ! Edit header and run scripts/sipify.pl again * + ************************************************************************/ + + + + +class QgsReportSectionLayout : QgsAbstractReportSection +{ +%Docstring + A report section consisting of a single QgsLayout body. + +.. warning:: + + This is not considered stable API, and may change in future QGIS releases. It is +exposed to the Python bindings for unit testing purposes only. + +.. versionadded:: 3.0 +%End + +%TypeHeaderCode +#include "qgsreportsectionlayout.h" +%End + public: + + QgsReportSectionLayout( QgsAbstractReportSection *parent = 0 ); +%Docstring +Constructor for QgsReportSectionLayout, attached to the specified ``parent`` section. +Note that ownership is not transferred to ``parent``. +%End + + QgsLayout *body(); +%Docstring +Returns the body layout for the section. + +.. seealso:: :py:func:`setBody()` +%End + + void setBody( QgsLayout *body /Transfer/ ); +%Docstring +Sets the ``body`` layout for the section. Ownership of ``body`` +is transferred to the report section. + +.. seealso:: :py:func:`body()` +%End + + virtual QgsReportSectionLayout *clone() const /Factory/; + + virtual bool beginRender(); + + virtual QgsLayout *nextBody( bool &ok ); + + +}; + + +/************************************************************************ + * This file has been generated automatically from * + * * + * src/core/layout/qgsreportsectionlayout.h * + * * + * Do not edit manually ! Edit header and run scripts/sipify.pl again * + ************************************************************************/ diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index d1a766009b0..ab67925a881 100755 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -415,6 +415,9 @@ SET(QGIS_CORE_SRCS layout/qgslayoutserializableobject.cpp layout/qgslayoutsize.cpp layout/qgsprintlayout.cpp + layout/qgsreport.cpp + layout/qgsreportsectionfieldgroup.cpp + layout/qgsreportsectionlayout.cpp pal/costcalculator.cpp pal/feature.cpp @@ -1045,6 +1048,9 @@ SET(QGIS_CORE_HDRS layout/qgslayoutsnapper.h layout/qgslayoutundocommand.h layout/qgslayoututils.h + layout/qgsreport.h + layout/qgsreportsectionfieldgroup.h + layout/qgsreportsectionlayout.h metadata/qgslayermetadata.h metadata/qgslayermetadatavalidator.h diff --git a/src/core/layout/qgsabstractreportsection.cpp b/src/core/layout/qgsabstractreportsection.cpp index 77ab746205b..04b680fe1e5 100644 --- a/src/core/layout/qgsabstractreportsection.cpp +++ b/src/core/layout/qgsabstractreportsection.cpp @@ -16,6 +16,7 @@ #include "qgsabstractreportsection.h" #include "qgslayout.h" +#include "qgsreport.h" ///@cond NOT_STABLE @@ -44,7 +45,7 @@ QgsProject *QgsAbstractReportSection::project() return nullptr; } -void QgsAbstractReportSection::setContext( const QgsReportContext &context ) +void QgsAbstractReportSection::setContext( const QgsReportSectionContext &context ) { mContext = context; for ( QgsAbstractReportSection *section : qgis::as_const( mChildren ) ) @@ -274,173 +275,5 @@ void QgsAbstractReportSection::copyCommonProperties( QgsAbstractReportSection *d } } - -// QgsReport - -QgsReport::QgsReport( QgsProject *project ) - : QgsAbstractReportSection( nullptr ) - , mProject( project ) -{} - -QgsReport *QgsReport::clone() const -{ - std::unique_ptr< QgsReport > copy = qgis::make_unique< QgsReport >( mProject ); - copyCommonProperties( copy.get() ); - return copy.release(); -} - -// -// QgsReportSectionLayout -// - -QgsReportSectionLayout::QgsReportSectionLayout( QgsAbstractReportSection *parent ) - : QgsAbstractReportSection( parent ) -{} - -QgsReportSectionLayout *QgsReportSectionLayout::clone() const -{ - std::unique_ptr< QgsReportSectionLayout > copy = qgis::make_unique< QgsReportSectionLayout >( nullptr ); - copyCommonProperties( copy.get() ); - - if ( mBody ) - { - copy->mBody.reset( mBody->clone() ); - } - else - copy->mBody.reset(); - - return copy.release(); -} - -bool QgsReportSectionLayout::beginRender() -{ - mExportedBody = false; - return QgsAbstractReportSection::beginRender(); -} - -QgsLayout *QgsReportSectionLayout::nextBody( bool &ok ) -{ - if ( !mExportedBody && mBody ) - { - mExportedBody = true; - ok = true; - return mBody.get(); - } - else - { - ok = false; - return nullptr; - } -} - -// -// QgsReportSectionFieldGroup -// - -QgsReportSectionFieldGroup::QgsReportSectionFieldGroup( QgsAbstractReportSection *parent ) - : QgsAbstractReportSection( parent ) -{ - -} - -QgsReportSectionFieldGroup *QgsReportSectionFieldGroup::clone() const -{ - std::unique_ptr< QgsReportSectionFieldGroup > copy = qgis::make_unique< QgsReportSectionFieldGroup >( nullptr ); - copyCommonProperties( copy.get() ); - - if ( mBody ) - { - copy->mBody.reset( mBody->clone() ); - } - else - copy->mBody.reset(); - - copy->setLayer( mCoverageLayer.get() ); - copy->setField( mField ); - - return copy.release(); -} - -bool QgsReportSectionFieldGroup::beginRender() -{ - if ( !mCoverageLayer.get() ) - return false; - - if ( !mField.isEmpty() ) - { - mFieldIndex = mCoverageLayer->fields().lookupField( mField ); - if ( mFieldIndex < 0 ) - return false; - - if ( mBody ) - mBody->reportContext().setLayer( mCoverageLayer.get() ); - - mFeatures = QgsFeatureIterator(); - } - return QgsAbstractReportSection::beginRender(); -} - -QgsLayout *QgsReportSectionFieldGroup::nextBody( bool &ok ) -{ - if ( !mFeatures.isValid() ) - { - QgsFeatureRequest request; - QString filter = context().layerFilters.value( mCoverageLayer.get() ); - if ( !filter.isEmpty() ) - request.setFilterExpression( filter ); - request.addOrderBy( mField, true ); - mFeatures = mCoverageLayer->getFeatures( request ); - } - - QgsFeature f; - QVariant currentValue; - bool first = true; - while ( first || ( !mBody && mEncounteredValues.contains( currentValue ) ) ) - { - if ( !mFeatures.nextFeature( f ) ) - { - // no features left for this iteration - mFeatures = QgsFeatureIterator(); - ok = false; - return nullptr; - } - - first = false; - currentValue = f.attribute( mFieldIndex ); - } - - mEncounteredValues.insert( currentValue ); - - QgsReportContext c = context(); - QString currentFilter = c.layerFilters.value( mCoverageLayer.get() ); - QString thisFilter = QgsExpression::createFieldEqualityExpression( mField, currentValue ); - QString newFilter = currentFilter.isEmpty() ? thisFilter : QStringLiteral( "(%1) AND (%2)" ).arg( currentFilter, thisFilter ); - c.layerFilters[ mCoverageLayer.get() ] = newFilter; - - const QList< QgsAbstractReportSection * > sections = children(); - for ( QgsAbstractReportSection *section : qgis::as_const( sections ) ) - { - section->setContext( c ); - } - - ok = true; - - if ( mBody ) - { - mBody->reportContext().blockSignals( true ); - mBody->reportContext().setLayer( mCoverageLayer.get() ); - mBody->reportContext().blockSignals( false ); - mBody->reportContext().setFeature( f ); - } - - return mBody.get(); -} - -void QgsReportSectionFieldGroup::reset() -{ - QgsAbstractReportSection::reset(); - mEncounteredValues.clear(); -} - ///@endcond diff --git a/src/core/layout/qgsabstractreportsection.h b/src/core/layout/qgsabstractreportsection.h index 88340a84c29..8907bba566c 100644 --- a/src/core/layout/qgsabstractreportsection.h +++ b/src/core/layout/qgsabstractreportsection.h @@ -26,10 +26,19 @@ // This is not considered stable API - it is exposed to python bindings only for unit testing! -class CORE_EXPORT QgsReportContext +/** + * \ingroup core + * \class QgsReportSectionContext + * \brief Current context for a report section. + * \warning This is not considered stable API, and may change in future QGIS releases. It is + * exposed to the Python bindings for unit testing purposes only. + * \since QGIS 3.0 + */ +class CORE_EXPORT QgsReportSectionContext { public: + //! Current layer filters QMap< QgsVectorLayer *, QString > layerFilters SIP_SKIP; }; @@ -225,13 +234,13 @@ class CORE_EXPORT QgsAbstractReportSection : public QgsAbstractLayoutIterator * Sets the current \a context for this section. * \see context() */ - void setContext( const QgsReportContext &context ); + void setContext( const QgsReportSectionContext &context ); /** * Returns the current context for this section. * \see setContext() */ - const QgsReportContext &context() const { return mContext; } + const QgsReportSectionContext &context() const { return mContext; } protected: @@ -272,169 +281,13 @@ class CORE_EXPORT QgsAbstractReportSection : public QgsAbstractLayoutIterator QList< QgsAbstractReportSection * > mChildren; - QgsReportContext mContext; + QgsReportSectionContext mContext; #ifdef SIP_RUN QgsAbstractReportSection( const QgsAbstractReportSection &other ); #endif }; -/** - * \ingroup core - * \class QgsReportSectionLayout - * \brief A report section consisting of a single QgsLayout body. - * \warning This is not considered stable API, and may change in future QGIS releases. It is - * exposed to the Python bindings for unit testing purposes only. - * \since QGIS 3.0 - */ -class CORE_EXPORT QgsReportSectionLayout : public QgsAbstractReportSection -{ - public: - - /** - * Constructor for QgsReportSectionLayout, attached to the specified \a parent section. - * Note that ownership is not transferred to \a parent. - */ - QgsReportSectionLayout( QgsAbstractReportSection *parent = nullptr ); - - /** - * Returns the body layout for the section. - * \see setBody() - */ - QgsLayout *body() { return mBody.get(); } - - /** - * Sets the \a body layout for the section. Ownership of \a body - * is transferred to the report section. - * \see body() - */ - void setBody( QgsLayout *body SIP_TRANSFER ) { mBody.reset( body ); } - - QgsReportSectionLayout *clone() const override SIP_FACTORY; - bool beginRender() override; - QgsLayout *nextBody( bool &ok ) override; - - private: - - bool mExportedBody = false; - std::unique_ptr< QgsLayout > mBody; - -}; - -/** - * \ingroup core - * \class QgsReportSectionFieldGroup - * \brief A report section consisting of a features - * - * \warning This is not considered stable API, and may change in future QGIS releases. It is - * exposed to the Python bindings for unit testing purposes only. - * - * \since QGIS 3.0 - */ -class CORE_EXPORT QgsReportSectionFieldGroup : public QgsAbstractReportSection -{ - public: - - /** - * Constructor for QgsReportSectionFieldGroup, attached to the specified \a parent section. - * Note that ownership is not transferred to \a parent. - */ - QgsReportSectionFieldGroup( QgsAbstractReportSection *parent = nullptr ); - - /** - * Returns the body layout for the section. - * \see setBody() - */ - QgsLayout *body() { return mBody.get(); } - - /** - * Sets the \a body layout for the section. Ownership of \a body - * is transferred to the report section. - * \see body() - */ - void setBody( QgsLayout *body SIP_TRANSFER ) { mBody.reset( body ); } - - /** - * Returns the vector layer associated with this section. - * \see setLayer() - */ - QgsVectorLayer *layer() { return mCoverageLayer.get(); } - - /** - * Sets the vector \a layer associated with this section. - * \see layer() - */ - void setLayer( QgsVectorLayer *layer ) { mCoverageLayer = layer; } - - /** - * Returns the field associated with this section. - * \see setField() - */ - QString field() const { return mField; } - - /** - * Sets the \a field associated with this section. - * \see field() - */ - void setField( const QString &field ) { mField = field; } - - QgsReportSectionFieldGroup *clone() const override SIP_FACTORY; - bool beginRender() override; - QgsLayout *nextBody( bool &ok ) override; - void reset() override; - - private: - - QgsVectorLayerRef mCoverageLayer; - QString mField; - int mFieldIndex = -1; - QgsFeatureIterator mFeatures; - QSet< QVariant > mEncounteredValues; - - std::unique_ptr< QgsLayout > mBody; - -}; - - -/** - * \ingroup core - * \class QgsReport - * \brief Represents a report for use with the QgsLayout engine. - * - * Reports consist of multiple sections, represented by QgsAbstractReportSection - * subclasses. - * - * \warning This is not considered stable API, and may change in future QGIS releases. It is - * exposed to the Python bindings for unit testing purposes only. - * - * \since QGIS 3.0 - */ -class CORE_EXPORT QgsReport : public QgsAbstractReportSection -{ - - public: - - /** - * Constructor for QgsReport, associated with the specified - * \a project. - * - * Note that ownership is not transferred to \a project. - */ - QgsReport( QgsProject *project ); - - /** - * Returns the associated project. - */ - QgsProject *project() { return mProject; } - - QgsReport *clone() const override SIP_FACTORY; - - private: - - QgsProject *mProject = nullptr; - -}; - ///@endcond #endif //QGSABSTRACTREPORTSECTION_H diff --git a/src/core/layout/qgsreport.cpp b/src/core/layout/qgsreport.cpp new file mode 100644 index 00000000000..c1859381893 --- /dev/null +++ b/src/core/layout/qgsreport.cpp @@ -0,0 +1,35 @@ +/*************************************************************************** + qgsreport.cpp + -------------------- + begin : December 2017 + copyright : (C) 2017 by Nyall Dawson + email : nyall dot dawson at gmail dot com + ***************************************************************************/ +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#include "qgsreport.h" +#include "qgslayout.h" + +///@cond NOT_STABLE + +QgsReport::QgsReport( QgsProject *project ) + : QgsAbstractReportSection( nullptr ) + , mProject( project ) +{} + +QgsReport *QgsReport::clone() const +{ + std::unique_ptr< QgsReport > copy = qgis::make_unique< QgsReport >( mProject ); + copyCommonProperties( copy.get() ); + return copy.release(); +} + +///@endcond + diff --git a/src/core/layout/qgsreport.h b/src/core/layout/qgsreport.h new file mode 100644 index 00000000000..f912e7d6b95 --- /dev/null +++ b/src/core/layout/qgsreport.h @@ -0,0 +1,68 @@ +/*************************************************************************** + qgsreport.h + --------------------------- + begin : December 2017 + copyright : (C) 2017 by Nyall Dawson + email : nyall dot dawson at gmail dot com + ***************************************************************************/ +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ +#ifndef QGSREPORT_H +#define QGSREPORT_H + +#include "qgis_core.h" +#include "qgsabstractreportsection.h" + + +///@cond NOT_STABLE + +// This is not considered stable API - it is exposed to python bindings only for unit testing! + +/** + * \ingroup core + * \class QgsReport + * \brief Represents a report for use with the QgsLayout engine. + * + * Reports consist of multiple sections, represented by QgsAbstractReportSection + * subclasses. + * + * \warning This is not considered stable API, and may change in future QGIS releases. It is + * exposed to the Python bindings for unit testing purposes only. + * + * \since QGIS 3.0 + */ +class CORE_EXPORT QgsReport : public QgsAbstractReportSection +{ + + public: + + /** + * Constructor for QgsReport, associated with the specified + * \a project. + * + * Note that ownership is not transferred to \a project. + */ + QgsReport( QgsProject *project ); + + /** + * Returns the associated project. + */ + QgsProject *project() { return mProject; } + + QgsReport *clone() const override SIP_FACTORY; + + private: + + QgsProject *mProject = nullptr; + +}; + +///@endcond + +#endif //QGSREPORT_H diff --git a/src/core/layout/qgsreportsectionfieldgroup.cpp b/src/core/layout/qgsreportsectionfieldgroup.cpp new file mode 100644 index 00000000000..09477167c1f --- /dev/null +++ b/src/core/layout/qgsreportsectionfieldgroup.cpp @@ -0,0 +1,147 @@ +/*************************************************************************** + qgsreportsectionfieldgroup.cpp + -------------------- + begin : December 2017 + copyright : (C) 2017 by Nyall Dawson + email : nyall dot dawson at gmail dot com + ***************************************************************************/ +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#include "qgsreportsectionfieldgroup.h" +#include "qgslayout.h" + +///@cond NOT_STABLE + +QgsReportSectionFieldGroup::QgsReportSectionFieldGroup( QgsAbstractReportSection *parent ) + : QgsAbstractReportSection( parent ) +{ + +} + +QgsReportSectionFieldGroup *QgsReportSectionFieldGroup::clone() const +{ + std::unique_ptr< QgsReportSectionFieldGroup > copy = qgis::make_unique< QgsReportSectionFieldGroup >( nullptr ); + copyCommonProperties( copy.get() ); + + if ( mBody ) + { + copy->mBody.reset( mBody->clone() ); + } + else + copy->mBody.reset(); + + copy->setLayer( mCoverageLayer.get() ); + copy->setField( mField ); + + return copy.release(); +} + +bool QgsReportSectionFieldGroup::beginRender() +{ + if ( !mCoverageLayer.get() ) + return false; + + if ( !mField.isEmpty() ) + { + mFieldIndex = mCoverageLayer->fields().lookupField( mField ); + if ( mFieldIndex < 0 ) + return false; + + if ( mBody ) + mBody->reportContext().setLayer( mCoverageLayer.get() ); + + mFeatures = QgsFeatureIterator(); + } + return QgsAbstractReportSection::beginRender(); +} + +QgsLayout *QgsReportSectionFieldGroup::nextBody( bool &ok ) +{ + if ( !mFeatures.isValid() ) + { + mFeatures = mCoverageLayer->getFeatures( buildFeatureRequest() ); + } + + QgsFeature f = getNextFeature(); + if ( !f.isValid() ) + { + // no features left for this iteration + mFeatures = QgsFeatureIterator(); + ok = false; + return nullptr; + } + + updateChildContexts( f ); + + ok = true; + if ( mBody ) + { + mBody->reportContext().blockSignals( true ); + mBody->reportContext().setLayer( mCoverageLayer.get() ); + mBody->reportContext().blockSignals( false ); + mBody->reportContext().setFeature( f ); + } + + return mBody.get(); +} + +void QgsReportSectionFieldGroup::reset() +{ + QgsAbstractReportSection::reset(); + mEncounteredValues.clear(); +} + +QgsFeatureRequest QgsReportSectionFieldGroup::buildFeatureRequest() const +{ + QgsFeatureRequest request; + QString filter = context().layerFilters.value( mCoverageLayer.get() ); + if ( !filter.isEmpty() ) + request.setFilterExpression( filter ); + request.addOrderBy( mField, true ); + return request; +} + +QgsFeature QgsReportSectionFieldGroup::getNextFeature() +{ + QgsFeature f; + QVariant currentValue; + bool first = true; + while ( first || ( !mBody && mEncounteredValues.contains( currentValue ) ) ) + { + if ( !mFeatures.nextFeature( f ) ) + { + return QgsFeature(); + } + + first = false; + currentValue = f.attribute( mFieldIndex ); + } + + mEncounteredValues.insert( currentValue ); + return f; +} + +void QgsReportSectionFieldGroup::updateChildContexts( const QgsFeature &feature ) +{ + QgsReportSectionContext c = context(); + QString currentFilter = c.layerFilters.value( mCoverageLayer.get() ); + QString thisFilter = QgsExpression::createFieldEqualityExpression( mField, feature.attribute( mFieldIndex ) ); + QString newFilter = currentFilter.isEmpty() ? thisFilter : QStringLiteral( "(%1) AND (%2)" ).arg( currentFilter, thisFilter ); + c.layerFilters[ mCoverageLayer.get() ] = newFilter; + + const QList< QgsAbstractReportSection * > sections = children(); + for ( QgsAbstractReportSection *section : qgis::as_const( sections ) ) + { + section->setContext( c ); + } +} + +///@endcond + diff --git a/src/core/layout/qgsreportsectionfieldgroup.h b/src/core/layout/qgsreportsectionfieldgroup.h new file mode 100644 index 00000000000..6075376ac99 --- /dev/null +++ b/src/core/layout/qgsreportsectionfieldgroup.h @@ -0,0 +1,109 @@ +/*************************************************************************** + qgsreportsectionfieldgroup.h + --------------------------- + begin : December 2017 + copyright : (C) 2017 by Nyall Dawson + email : nyall dot dawson at gmail dot com + ***************************************************************************/ +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ +#ifndef QGSREPORTSECTIONFIELDGROUP_H +#define QGSREPORTSECTIONFIELDGROUP_H + +#include "qgis_core.h" +#include "qgsabstractreportsection.h" + + +///@cond NOT_STABLE + +// This is not considered stable API - it is exposed to python bindings only for unit testing! + +/** + * \ingroup core + * \class QgsReportSectionFieldGroup + * \brief A report section consisting of a features + * + * \warning This is not considered stable API, and may change in future QGIS releases. It is + * exposed to the Python bindings for unit testing purposes only. + * + * \since QGIS 3.0 + */ +class CORE_EXPORT QgsReportSectionFieldGroup : public QgsAbstractReportSection +{ + public: + + /** + * Constructor for QgsReportSectionFieldGroup, attached to the specified \a parent section. + * Note that ownership is not transferred to \a parent. + */ + QgsReportSectionFieldGroup( QgsAbstractReportSection *parent = nullptr ); + + /** + * Returns the body layout for the section. + * \see setBody() + */ + QgsLayout *body() { return mBody.get(); } + + /** + * Sets the \a body layout for the section. Ownership of \a body + * is transferred to the report section. + * \see body() + */ + void setBody( QgsLayout *body SIP_TRANSFER ) { mBody.reset( body ); } + + /** + * Returns the vector layer associated with this section. + * \see setLayer() + */ + QgsVectorLayer *layer() { return mCoverageLayer.get(); } + + /** + * Sets the vector \a layer associated with this section. + * \see layer() + */ + void setLayer( QgsVectorLayer *layer ) { mCoverageLayer = layer; } + + /** + * Returns the field associated with this section. + * \see setField() + */ + QString field() const { return mField; } + + /** + * Sets the \a field associated with this section. + * \see field() + */ + void setField( const QString &field ) { mField = field; } + + QgsReportSectionFieldGroup *clone() const override SIP_FACTORY; + bool beginRender() override; + QgsLayout *nextBody( bool &ok ) override; + void reset() override; + + private: + + QgsVectorLayerRef mCoverageLayer; + QString mField; + int mFieldIndex = -1; + QgsFeatureIterator mFeatures; + QSet< QVariant > mEncounteredValues; + + std::unique_ptr< QgsLayout > mBody; + + QgsFeatureRequest buildFeatureRequest() const; + + QgsFeature getNextFeature(); + void updateChildContexts( const QgsFeature &feature ); + +}; + + +///@endcond + +#endif //QGSREPORTSECTIONFIELDGROUP_H diff --git a/src/core/layout/qgsreportsectionlayout.cpp b/src/core/layout/qgsreportsectionlayout.cpp new file mode 100644 index 00000000000..fa5d85615b5 --- /dev/null +++ b/src/core/layout/qgsreportsectionlayout.cpp @@ -0,0 +1,63 @@ +/*************************************************************************** + qgsreportsectionlayout.cpp + -------------------- + begin : December 2017 + copyright : (C) 2017 by Nyall Dawson + email : nyall dot dawson at gmail dot com + ***************************************************************************/ +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#include "qgsreportsectionlayout.h" +#include "qgslayout.h" + +///@cond NOT_STABLE + +QgsReportSectionLayout::QgsReportSectionLayout( QgsAbstractReportSection *parent ) + : QgsAbstractReportSection( parent ) +{} + +QgsReportSectionLayout *QgsReportSectionLayout::clone() const +{ + std::unique_ptr< QgsReportSectionLayout > copy = qgis::make_unique< QgsReportSectionLayout >( nullptr ); + copyCommonProperties( copy.get() ); + + if ( mBody ) + { + copy->mBody.reset( mBody->clone() ); + } + else + copy->mBody.reset(); + + return copy.release(); +} + +bool QgsReportSectionLayout::beginRender() +{ + mExportedBody = false; + return QgsAbstractReportSection::beginRender(); +} + +QgsLayout *QgsReportSectionLayout::nextBody( bool &ok ) +{ + if ( !mExportedBody && mBody ) + { + mExportedBody = true; + ok = true; + return mBody.get(); + } + else + { + ok = false; + return nullptr; + } +} + +///@endcond + diff --git a/src/core/layout/qgsreportsectionlayout.h b/src/core/layout/qgsreportsectionlayout.h new file mode 100644 index 00000000000..9ddd053c4ac --- /dev/null +++ b/src/core/layout/qgsreportsectionlayout.h @@ -0,0 +1,70 @@ +/*************************************************************************** + qgsreportsectionlayout.h + --------------------------- + begin : December 2017 + copyright : (C) 2017 by Nyall Dawson + email : nyall dot dawson at gmail dot com + ***************************************************************************/ +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ +#ifndef QGSREPORTSECTIONLAYOUT_H +#define QGSREPORTSECTIONLAYOUT_H + +#include "qgis_core.h" +#include "qgsabstractreportsection.h" + +///@cond NOT_STABLE + +// This is not considered stable API - it is exposed to python bindings only for unit testing! + +/** + * \ingroup core + * \class QgsReportSectionLayout + * \brief A report section consisting of a single QgsLayout body. + * \warning This is not considered stable API, and may change in future QGIS releases. It is + * exposed to the Python bindings for unit testing purposes only. + * \since QGIS 3.0 + */ +class CORE_EXPORT QgsReportSectionLayout : public QgsAbstractReportSection +{ + public: + + /** + * Constructor for QgsReportSectionLayout, attached to the specified \a parent section. + * Note that ownership is not transferred to \a parent. + */ + QgsReportSectionLayout( QgsAbstractReportSection *parent = nullptr ); + + /** + * Returns the body layout for the section. + * \see setBody() + */ + QgsLayout *body() { return mBody.get(); } + + /** + * Sets the \a body layout for the section. Ownership of \a body + * is transferred to the report section. + * \see body() + */ + void setBody( QgsLayout *body SIP_TRANSFER ) { mBody.reset( body ); } + + QgsReportSectionLayout *clone() const override SIP_FACTORY; + bool beginRender() override; + QgsLayout *nextBody( bool &ok ) override; + + private: + + bool mExportedBody = false; + std::unique_ptr< QgsLayout > mBody; + +}; + +///@endcond + +#endif //QGSREPORTSECTIONLAYOUT_H