mirror of
https://github.com/qgis/QGIS.git
synced 2025-10-15 00:02:52 -04:00
[layouts] Improve logic of splitting layouts into separate layers when
exporting to a multi-layer format Now, items are either - placed onto the same layer as other items (simple items like labels, lines, pictures) - placed onto the same layer as only other items of equal types (semi-complex items like scalebars or pages) - placed onto their own unique layers (complex items like legends, maps) Plus lots of tests covering this, where previously there was few
This commit is contained in:
parent
b6ab6df69f
commit
208beb7f8c
@ -115,6 +115,8 @@ Returns whether the frame is empty.
|
||||
|
||||
virtual QgsExpressionContext createExpressionContext() const;
|
||||
|
||||
virtual ExportLayerBehavior exportLayerBehavior() const;
|
||||
|
||||
|
||||
protected:
|
||||
|
||||
|
@ -384,6 +384,25 @@ Sets the item's parent ``group``.
|
||||
.. seealso:: :py:func:`isGroupMember`
|
||||
|
||||
.. seealso:: :py:func:`parentGroup`
|
||||
%End
|
||||
|
||||
enum ExportLayerBehavior
|
||||
{
|
||||
CanGroupWithAnyOtherItem,
|
||||
CanGroupWithItemsOfSameType,
|
||||
MustPlaceInOwnLayer,
|
||||
ItemContainsSubLayers,
|
||||
};
|
||||
|
||||
virtual ExportLayerBehavior exportLayerBehavior() const;
|
||||
%Docstring
|
||||
Returns the behavior of this item during exporting to layered exports (e.g. SVG).
|
||||
|
||||
.. seealso:: :py:func:`numberExportLayers`
|
||||
|
||||
.. seealso:: :py:func:`exportLayerDetails`
|
||||
|
||||
.. versionadded:: 3.10
|
||||
%End
|
||||
|
||||
virtual int numberExportLayers() const;
|
||||
@ -394,6 +413,10 @@ Returns 0 if this item is to be placed on the same layer as the previous item,
|
||||
|
||||
Items which require multiply layers should check QgsLayoutContext.currentExportLayer() during
|
||||
their rendering to determine which layer should be drawn.
|
||||
|
||||
.. seealso:: :py:func:`exportLayerBehavior`
|
||||
|
||||
.. seealso:: :py:func:`exportLayerDetails`
|
||||
%End
|
||||
|
||||
struct ExportLayerDetail
|
||||
|
@ -71,6 +71,7 @@ Returns a list of items contained by the group.
|
||||
|
||||
virtual void finalizeRestoreFromXml();
|
||||
|
||||
virtual ExportLayerBehavior exportLayerBehavior() const;
|
||||
|
||||
protected:
|
||||
virtual void draw( QgsLayoutItemRenderContext &context );
|
||||
|
@ -527,6 +527,8 @@ Returns the legend's renderer settings object.
|
||||
|
||||
virtual QgsExpressionContext createExpressionContext() const;
|
||||
|
||||
virtual ExportLayerBehavior exportLayerBehavior() const;
|
||||
|
||||
|
||||
public slots:
|
||||
|
||||
|
@ -89,6 +89,8 @@ The caller takes responsibility for deleting the returned object.
|
||||
|
||||
virtual int numberExportLayers() const;
|
||||
|
||||
virtual ExportLayerBehavior exportLayerBehavior() const;
|
||||
|
||||
virtual QgsLayoutItem::ExportLayerDetail exportLayerDetails( int layer ) const;
|
||||
|
||||
virtual void setFrameStrokeWidth( QgsLayoutMeasurement width );
|
||||
|
@ -97,6 +97,8 @@ page orientation.
|
||||
|
||||
virtual QgsAbstractLayoutUndoCommand *createCommand( const QString &text, int id, QUndoCommand *parent = 0 ) /Factory/;
|
||||
|
||||
virtual ExportLayerBehavior exportLayerBehavior() const;
|
||||
|
||||
|
||||
public slots:
|
||||
|
||||
|
@ -547,6 +547,8 @@ Adjusts the scale bar box size and updates the item.
|
||||
|
||||
virtual bool accept( QgsStyleEntityVisitorInterface *visitor ) const;
|
||||
|
||||
virtual ExportLayerBehavior exportLayerBehavior() const;
|
||||
|
||||
|
||||
protected:
|
||||
|
||||
|
@ -880,9 +880,9 @@ QgsLayoutExporter::ExportResult QgsLayoutExporter::exportToSvg( const QString &f
|
||||
Qt::IntersectsItemBoundingRect,
|
||||
Qt::AscendingOrder );
|
||||
|
||||
auto exportFunc = [this, &settings, width, height, i, bounds, fileName, &svg, &svgDocRoot]( unsigned int layerId, const QString & layerName )->QgsLayoutExporter::ExportResult
|
||||
auto exportFunc = [this, &settings, width, height, i, bounds, fileName, &svg, &svgDocRoot]( unsigned int layerId, const QgsLayoutItem::ExportLayerDetail & layerDetail )->QgsLayoutExporter::ExportResult
|
||||
{
|
||||
return renderToLayeredSvg( settings, width, height, i, bounds, fileName, layerId, layerName, svg, svgDocRoot, settings.exportMetadata );
|
||||
return renderToLayeredSvg( settings, width, height, i, bounds, fileName, layerId, layerDetail.name, svg, svgDocRoot, settings.exportMetadata );
|
||||
};
|
||||
ExportResult res = handleLayeredExport( items, exportFunc );
|
||||
if ( res != Success )
|
||||
@ -1127,7 +1127,7 @@ void QgsLayoutExporter::updatePrinterPageSize( QgsLayout *layout, QPrinter &prin
|
||||
printer.setPageMargins( QMarginsF( 0, 0, 0, 0 ) );
|
||||
}
|
||||
|
||||
QgsLayoutExporter::ExportResult QgsLayoutExporter::renderToLayeredSvg( const SvgExportSettings &settings, double width, double height, int page, const QRectF &bounds, const QString &filename, int svgLayerId, const QString &layerName, QDomDocument &svg, QDomNode &svgDocRoot, bool includeMetadata ) const
|
||||
QgsLayoutExporter::ExportResult QgsLayoutExporter::renderToLayeredSvg( const SvgExportSettings &settings, double width, double height, int page, const QRectF &bounds, const QString &filename, unsigned int svgLayerId, const QString &layerName, QDomDocument &svg, QDomNode &svgDocRoot, bool includeMetadata ) const
|
||||
{
|
||||
QBuffer svgBuffer;
|
||||
{
|
||||
@ -1430,52 +1430,123 @@ bool QgsLayoutExporter::georeferenceOutputPrivate( const QString &file, QgsLayou
|
||||
}
|
||||
|
||||
QgsLayoutExporter::ExportResult QgsLayoutExporter::handleLayeredExport( const QList<QGraphicsItem *> &items,
|
||||
const std::function<QgsLayoutExporter::ExportResult( unsigned int, const QString & )> &exportFunc )
|
||||
const std::function<QgsLayoutExporter::ExportResult( unsigned int, const QgsLayoutItem::ExportLayerDetail & )> &exportFunc )
|
||||
{
|
||||
LayoutItemHider itemHider( items );
|
||||
( void )itemHider;
|
||||
|
||||
int layoutItemLayerIdx = 0;
|
||||
auto it = items.constBegin();
|
||||
for ( unsigned int layerId = 1; it != items.constEnd(); ++layerId )
|
||||
int prevType = -1;
|
||||
QgsLayoutItem::ExportLayerBehavior prevItemBehavior = QgsLayoutItem::CanGroupWithAnyOtherItem;
|
||||
unsigned int layerId = 1;
|
||||
QgsLayoutItem::ExportLayerDetail layerDetails;
|
||||
layerDetails.name = QObject::tr( "Layer %1" ).arg( layerId );
|
||||
itemHider.hideAll();
|
||||
bool haveUnexportedItems = false;
|
||||
for ( auto it = items.constBegin(); it != items.constEnd(); ++it )
|
||||
{
|
||||
itemHider.hideAll();
|
||||
QgsLayoutItem *layoutItem = dynamic_cast<QgsLayoutItem *>( *it );
|
||||
const QString layerName = QObject::tr( "Layer %1" ).arg( layerId );
|
||||
if ( layoutItem && layoutItem->numberExportLayers() > 0 )
|
||||
|
||||
bool canPlaceInExistingLayer = false;
|
||||
if ( layoutItem )
|
||||
{
|
||||
layoutItem->show();
|
||||
mLayout->renderContext().setCurrentExportLayer( layoutItemLayerIdx );
|
||||
++layoutItemLayerIdx;
|
||||
switch ( layoutItem->exportLayerBehavior() )
|
||||
{
|
||||
case QgsLayoutItem::CanGroupWithAnyOtherItem:
|
||||
{
|
||||
switch ( prevItemBehavior )
|
||||
{
|
||||
case QgsLayoutItem::CanGroupWithAnyOtherItem:
|
||||
canPlaceInExistingLayer = true;
|
||||
break;
|
||||
|
||||
case QgsLayoutItem::CanGroupWithItemsOfSameType:
|
||||
canPlaceInExistingLayer = prevType == -1 || prevType == layoutItem->type();
|
||||
break;
|
||||
|
||||
case QgsLayoutItem::MustPlaceInOwnLayer:
|
||||
case QgsLayoutItem::ItemContainsSubLayers:
|
||||
canPlaceInExistingLayer = false;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case QgsLayoutItem::CanGroupWithItemsOfSameType:
|
||||
{
|
||||
switch ( prevItemBehavior )
|
||||
{
|
||||
case QgsLayoutItem::CanGroupWithAnyOtherItem:
|
||||
case QgsLayoutItem::CanGroupWithItemsOfSameType:
|
||||
canPlaceInExistingLayer = prevType == -1 || prevType == layoutItem->type();
|
||||
break;
|
||||
|
||||
case QgsLayoutItem::MustPlaceInOwnLayer:
|
||||
case QgsLayoutItem::ItemContainsSubLayers:
|
||||
canPlaceInExistingLayer = false;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case QgsLayoutItem::MustPlaceInOwnLayer:
|
||||
case QgsLayoutItem::ItemContainsSubLayers:
|
||||
canPlaceInExistingLayer = false;
|
||||
break;
|
||||
}
|
||||
prevItemBehavior = layoutItem->exportLayerBehavior();
|
||||
prevType = layoutItem->type();
|
||||
}
|
||||
else
|
||||
{
|
||||
// show all items until the next item that renders on a separate layer
|
||||
for ( ; it != items.constEnd(); ++it )
|
||||
prevItemBehavior = QgsLayoutItem::MustPlaceInOwnLayer;
|
||||
}
|
||||
|
||||
if ( canPlaceInExistingLayer )
|
||||
{
|
||||
( *it )->show();
|
||||
haveUnexportedItems = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( haveUnexportedItems )
|
||||
{
|
||||
layoutItem = dynamic_cast<QgsLayoutItem *>( *it );
|
||||
if ( layoutItem && layoutItem->numberExportLayers() > 0 )
|
||||
ExportResult result = exportFunc( layerId, layerDetails );
|
||||
if ( result != Success )
|
||||
return result;
|
||||
layerId++;
|
||||
layerDetails.name = QObject::tr( "Layer %1" ).arg( layerId );
|
||||
haveUnexportedItems = false;
|
||||
}
|
||||
|
||||
itemHider.hideAll();
|
||||
( *it )->show();
|
||||
|
||||
if ( layoutItem && layoutItem->exportLayerBehavior() == QgsLayoutItem::ItemContainsSubLayers )
|
||||
{
|
||||
for ( int layoutItemLayerIdx = 0; layoutItemLayerIdx < layoutItem->numberExportLayers(); layoutItemLayerIdx++ )
|
||||
{
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
( *it )->show();
|
||||
mLayout->renderContext().setCurrentExportLayer( layoutItemLayerIdx );
|
||||
layerDetails = layoutItem->exportLayerDetails( layoutItemLayerIdx );
|
||||
ExportResult result = exportFunc( layerId, layerDetails );
|
||||
if ( result != Success )
|
||||
return result;
|
||||
layerId++;
|
||||
layerDetails.name = QObject::tr( "Layer %1" ).arg( layerId );
|
||||
}
|
||||
mLayout->renderContext().setCurrentExportLayer( -1 );
|
||||
haveUnexportedItems = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
haveUnexportedItems = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
ExportResult result = exportFunc( layerId, layerName );
|
||||
}
|
||||
if ( haveUnexportedItems )
|
||||
{
|
||||
ExportResult result = exportFunc( layerId, layerDetails );
|
||||
if ( result != Success )
|
||||
return result;
|
||||
|
||||
if ( layoutItem && layoutItem->numberExportLayers() > 0 && layoutItem->numberExportLayers() == layoutItemLayerIdx ) // restore and pass to next item
|
||||
{
|
||||
mLayout->renderContext().setCurrentExportLayer( -1 );
|
||||
layoutItemLayerIdx = 0;
|
||||
++it;
|
||||
}
|
||||
}
|
||||
return Success;
|
||||
}
|
||||
|
@ -585,7 +585,7 @@ class CORE_EXPORT QgsLayoutExporter
|
||||
static void updatePrinterPageSize( QgsLayout *layout, QPrinter &printer, int page );
|
||||
|
||||
ExportResult renderToLayeredSvg( const SvgExportSettings &settings, double width, double height, int page, const QRectF &bounds,
|
||||
const QString &filename, int svgLayerId, const QString &layerName,
|
||||
const QString &filename, unsigned int svgLayerId, const QString &layerName,
|
||||
QDomDocument &svg, QDomNode &svgDocRoot, bool includeMetadata ) const;
|
||||
|
||||
void appendMetadataToSvg( QDomDocument &svg ) const;
|
||||
@ -593,10 +593,11 @@ class CORE_EXPORT QgsLayoutExporter
|
||||
bool georeferenceOutputPrivate( const QString &file, QgsLayoutItemMap *referenceMap = nullptr,
|
||||
const QRectF &exportRegion = QRectF(), double dpi = -1, bool includeGeoreference = true, bool includeMetadata = false ) const;
|
||||
|
||||
ExportResult handleLayeredExport( const QList<QGraphicsItem *> &items, const std::function<QgsLayoutExporter::ExportResult( unsigned int layerId, const QString &layerName )> &exportFunc );
|
||||
ExportResult handleLayeredExport( const QList<QGraphicsItem *> &items, const std::function<QgsLayoutExporter::ExportResult( unsigned int layerId, const QgsLayoutItem::ExportLayerDetail &layerDetails )> &exportFunc );
|
||||
|
||||
static QgsVectorSimplifyMethod createExportSimplifyMethod();
|
||||
friend class TestQgsLayout;
|
||||
friend class TestQgsLayoutExporter;
|
||||
|
||||
};
|
||||
|
||||
|
@ -134,6 +134,10 @@ QgsExpressionContext QgsLayoutFrame::createExpressionContext() const
|
||||
return context;
|
||||
}
|
||||
|
||||
QgsLayoutItem::ExportLayerBehavior QgsLayoutFrame::exportLayerBehavior() const
|
||||
{
|
||||
return CanGroupWithItemsOfSameType;
|
||||
}
|
||||
|
||||
QString QgsLayoutFrame::displayName() const
|
||||
{
|
||||
|
@ -110,6 +110,7 @@ class CORE_EXPORT QgsLayoutFrame: public QgsLayoutItem
|
||||
bool isEmpty() const;
|
||||
|
||||
QgsExpressionContext createExpressionContext() const override;
|
||||
ExportLayerBehavior exportLayerBehavior() const override;
|
||||
|
||||
protected:
|
||||
|
||||
|
@ -244,6 +244,11 @@ void QgsLayoutItem::setParentGroup( QgsLayoutItemGroup *group )
|
||||
setFlag( QGraphicsItem::ItemIsSelectable, !static_cast< bool>( group ) ); //item in groups cannot be selected
|
||||
}
|
||||
|
||||
QgsLayoutItem::ExportLayerBehavior QgsLayoutItem::exportLayerBehavior() const
|
||||
{
|
||||
return CanGroupWithAnyOtherItem;
|
||||
}
|
||||
|
||||
QgsLayoutItem::ExportLayerDetail QgsLayoutItem::exportLayerDetails( int ) const
|
||||
{
|
||||
return QgsLayoutItem::ExportLayerDetail();
|
||||
|
@ -414,6 +414,26 @@ class CORE_EXPORT QgsLayoutItem : public QgsLayoutObject, public QGraphicsRectIt
|
||||
*/
|
||||
void setParentGroup( QgsLayoutItemGroup *group );
|
||||
|
||||
/**
|
||||
* Behavior of item when exporting to layered outputs.
|
||||
* \since QGIS 3.10
|
||||
*/
|
||||
enum ExportLayerBehavior
|
||||
{
|
||||
CanGroupWithAnyOtherItem, //!< Item can be placed on a layer with any other item (default behavior)
|
||||
CanGroupWithItemsOfSameType, //!< Item can only be placed on layers with other items of the same type, but multiple items of this type can be grouped together
|
||||
MustPlaceInOwnLayer, //!< Item must be placed in its own individual layer
|
||||
ItemContainsSubLayers, //!< Item contains multiple sublayers which must be individually exported
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns the behavior of this item during exporting to layered exports (e.g. SVG).
|
||||
* \see numberExportLayers()
|
||||
* \see exportLayerDetails()
|
||||
* \since QGIS 3.10
|
||||
*/
|
||||
virtual ExportLayerBehavior exportLayerBehavior() const;
|
||||
|
||||
/**
|
||||
* Returns the number of layers that this item requires for exporting during layered exports (e.g. SVG).
|
||||
* Returns 0 if this item is to be placed on the same layer as the previous item,
|
||||
@ -421,6 +441,9 @@ class CORE_EXPORT QgsLayoutItem : public QgsLayoutObject, public QGraphicsRectIt
|
||||
*
|
||||
* Items which require multiply layers should check QgsLayoutContext::currentExportLayer() during
|
||||
* their rendering to determine which layer should be drawn.
|
||||
*
|
||||
* \see exportLayerBehavior()
|
||||
* \see exportLayerDetails()
|
||||
*/
|
||||
virtual int numberExportLayers() const { return 0; }
|
||||
|
||||
|
@ -272,6 +272,11 @@ void QgsLayoutItemGroup::finalizeRestoreFromXml()
|
||||
resetBoundingRect();
|
||||
}
|
||||
|
||||
QgsLayoutItem::ExportLayerBehavior QgsLayoutItemGroup::exportLayerBehavior() const
|
||||
{
|
||||
return MustPlaceInOwnLayer;
|
||||
}
|
||||
|
||||
void QgsLayoutItemGroup::paint( QPainter *, const QStyleOptionGraphicsItem *, QWidget * )
|
||||
{
|
||||
}
|
||||
|
@ -75,7 +75,7 @@ class CORE_EXPORT QgsLayoutItemGroup: public QgsLayoutItem
|
||||
void paint( QPainter *painter, const QStyleOptionGraphicsItem *itemStyle, QWidget *pWidget ) override;
|
||||
|
||||
void finalizeRestoreFromXml() override;
|
||||
|
||||
ExportLayerBehavior exportLayerBehavior() const override;
|
||||
protected:
|
||||
void draw( QgsLayoutItemRenderContext &context ) override;
|
||||
bool writePropertiesToElement( QDomElement &parentElement, QDomDocument &document, const QgsReadWriteContext &context ) const override;
|
||||
|
@ -865,6 +865,11 @@ QgsExpressionContext QgsLayoutItemLegend::createExpressionContext() const
|
||||
return context;
|
||||
}
|
||||
|
||||
QgsLayoutItem::ExportLayerBehavior QgsLayoutItemLegend::exportLayerBehavior() const
|
||||
{
|
||||
return MustPlaceInOwnLayer;
|
||||
}
|
||||
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
#include "qgslayertreemodellegendnode.h"
|
||||
|
@ -496,6 +496,7 @@ class CORE_EXPORT QgsLayoutItemLegend : public QgsLayoutItem
|
||||
void finalizeRestoreFromXml() override;
|
||||
|
||||
QgsExpressionContext createExpressionContext() const override;
|
||||
ExportLayerBehavior exportLayerBehavior() const override;
|
||||
|
||||
public slots:
|
||||
|
||||
|
@ -993,6 +993,11 @@ int QgsLayoutItemMap::numberExportLayers() const
|
||||
+ ( frameEnabled() ? 1 : 0 );
|
||||
}
|
||||
|
||||
QgsLayoutItem::ExportLayerBehavior QgsLayoutItemMap::exportLayerBehavior() const
|
||||
{
|
||||
return ItemContainsSubLayers;
|
||||
}
|
||||
|
||||
QgsLayoutItem::ExportLayerDetail QgsLayoutItemMap::exportLayerDetails( int layer ) const
|
||||
{
|
||||
ExportLayerDetail detail;
|
||||
|
@ -117,6 +117,7 @@ class CORE_EXPORT QgsLayoutItemMap : public QgsLayoutItem
|
||||
// for now, map items behave a bit differently and don't implement draw. TODO - see if we can avoid this
|
||||
void paint( QPainter *painter, const QStyleOptionGraphicsItem *itemStyle, QWidget *pWidget ) override;
|
||||
int numberExportLayers() const override;
|
||||
ExportLayerBehavior exportLayerBehavior() const override;
|
||||
QgsLayoutItem::ExportLayerDetail exportLayerDetails( int layer ) const override;
|
||||
void setFrameStrokeWidth( QgsLayoutMeasurement width ) override;
|
||||
|
||||
|
@ -175,6 +175,11 @@ QgsAbstractLayoutUndoCommand *QgsLayoutItemPage::createCommand( const QString &t
|
||||
return new QgsLayoutItemPageUndoCommand( this, text, id, parent );
|
||||
}
|
||||
|
||||
QgsLayoutItem::ExportLayerBehavior QgsLayoutItemPage::exportLayerBehavior() const
|
||||
{
|
||||
return CanGroupWithItemsOfSameType;
|
||||
}
|
||||
|
||||
void QgsLayoutItemPage::redraw()
|
||||
{
|
||||
QgsLayoutItem::redraw();
|
||||
|
@ -124,6 +124,7 @@ class CORE_EXPORT QgsLayoutItemPage : public QgsLayoutItem
|
||||
QRectF boundingRect() const override;
|
||||
void attemptResize( const QgsLayoutSize &size, bool includesFrame = false ) override;
|
||||
QgsAbstractLayoutUndoCommand *createCommand( const QString &text, int id, QUndoCommand *parent = nullptr ) override SIP_FACTORY;
|
||||
ExportLayerBehavior exportLayerBehavior() const override;
|
||||
|
||||
public slots:
|
||||
|
||||
|
@ -884,3 +884,8 @@ bool QgsLayoutItemScaleBar::accept( QgsStyleEntityVisitorInterface *visitor ) co
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
QgsLayoutItem::ExportLayerBehavior QgsLayoutItemScaleBar::exportLayerBehavior() const
|
||||
{
|
||||
return CanGroupWithItemsOfSameType;
|
||||
}
|
||||
|
@ -467,6 +467,7 @@ class CORE_EXPORT QgsLayoutItemScaleBar: public QgsLayoutItem
|
||||
void refreshDataDefinedProperty( QgsLayoutObject::DataDefinedProperty property = QgsLayoutObject::AllProperties ) override;
|
||||
void finalizeRestoreFromXml() override;
|
||||
bool accept( QgsStyleEntityVisitorInterface *visitor ) const override;
|
||||
ExportLayerBehavior exportLayerBehavior() const override;
|
||||
|
||||
protected:
|
||||
|
||||
|
@ -136,6 +136,7 @@ SET(TESTS
|
||||
testqgslayout.cpp
|
||||
testqgslayoutatlas.cpp
|
||||
testqgslayoutcontext.cpp
|
||||
testqgslayoutexporter.cpp
|
||||
testqgslayouthtml.cpp
|
||||
testqgslayoutitem.cpp
|
||||
testqgslayoutitemgroup.cpp
|
||||
|
236
tests/src/core/testqgslayoutexporter.cpp
Normal file
236
tests/src/core/testqgslayoutexporter.cpp
Normal file
@ -0,0 +1,236 @@
|
||||
/***************************************************************************
|
||||
testqgslayoutexporter.cpp
|
||||
-----------------
|
||||
begin : August 2019
|
||||
copyright : (C) 2019 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 "qgslayout.h"
|
||||
#include "qgstest.h"
|
||||
#include "qgsproject.h"
|
||||
#include "qgslayoutexporter.h"
|
||||
#include "qgslayoutitemlabel.h"
|
||||
#include "qgslayoutitemshape.h"
|
||||
#include "qgslayoutitemscalebar.h"
|
||||
#include "qgslayoutitemmap.h"
|
||||
#include "qgsvectorlayer.h"
|
||||
#include "qgslayoutitemlegend.h"
|
||||
|
||||
class TestQgsLayoutExporter: public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
private slots:
|
||||
void initTestCase();// will be called before the first testfunction is executed.
|
||||
void cleanupTestCase();// will be called after the last testfunction was executed.
|
||||
void init();// will be called before each testfunction is executed.
|
||||
void cleanup();// will be called after every testfunction.
|
||||
void testHandleLayeredExport();
|
||||
|
||||
};
|
||||
|
||||
void TestQgsLayoutExporter::initTestCase()
|
||||
{
|
||||
QgsApplication::init();
|
||||
QgsApplication::initQgis();
|
||||
}
|
||||
|
||||
void TestQgsLayoutExporter::cleanupTestCase()
|
||||
{
|
||||
}
|
||||
|
||||
void TestQgsLayoutExporter::init()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void TestQgsLayoutExporter::cleanup()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void TestQgsLayoutExporter::testHandleLayeredExport()
|
||||
{
|
||||
QgsProject p;
|
||||
QgsLayout l( &p );
|
||||
QgsLayoutExporter exporter( &l );
|
||||
|
||||
QList< unsigned int > layerIds;
|
||||
QStringList layerNames;
|
||||
QStringList mapLayerIds;
|
||||
auto exportFunc = [&layerIds, &layerNames, &mapLayerIds]( unsigned int layerId, const QgsLayoutItem::ExportLayerDetail & layerDetail )->QgsLayoutExporter::ExportResult
|
||||
{
|
||||
layerIds << layerId;
|
||||
layerNames << layerDetail.name;
|
||||
mapLayerIds << layerDetail.mapLayerId;
|
||||
return QgsLayoutExporter::Success;
|
||||
};
|
||||
|
||||
QList< QGraphicsItem * > items;
|
||||
QgsLayoutExporter::ExportResult res = exporter.handleLayeredExport( items, exportFunc );
|
||||
QCOMPARE( res, QgsLayoutExporter::Success );
|
||||
QVERIFY( layerIds.isEmpty() );
|
||||
QVERIFY( layerNames.isEmpty() );
|
||||
QVERIFY( mapLayerIds.isEmpty() );
|
||||
|
||||
// add two pages to a layout
|
||||
QgsLayoutItemPage *page1 = new QgsLayoutItemPage( &l );
|
||||
items << page1;
|
||||
res = exporter.handleLayeredExport( items, exportFunc );
|
||||
QCOMPARE( res, QgsLayoutExporter::Success );
|
||||
QCOMPARE( layerIds, QList< unsigned int >() << 1 );
|
||||
QCOMPARE( layerNames, QStringList() << QStringLiteral( "Layer 1" ) );
|
||||
QCOMPARE( mapLayerIds, QStringList() << QString() );
|
||||
layerIds.clear();
|
||||
layerNames.clear();
|
||||
mapLayerIds.clear();
|
||||
|
||||
QgsLayoutItemPage *page2 = new QgsLayoutItemPage( &l );
|
||||
items << page2;
|
||||
res = exporter.handleLayeredExport( items, exportFunc );
|
||||
QCOMPARE( res, QgsLayoutExporter::Success );
|
||||
QCOMPARE( layerIds, QList< unsigned int >() << 1 );
|
||||
QCOMPARE( layerNames, QStringList() << QStringLiteral( "Layer 1" ) );
|
||||
QCOMPARE( mapLayerIds, QStringList() << QString() );
|
||||
layerIds.clear();
|
||||
layerNames.clear();
|
||||
mapLayerIds.clear();
|
||||
|
||||
QgsLayoutItemLabel *label = new QgsLayoutItemLabel( &l );
|
||||
items << label;
|
||||
res = exporter.handleLayeredExport( items, exportFunc );
|
||||
QCOMPARE( res, QgsLayoutExporter::Success );
|
||||
QCOMPARE( layerIds, QList< unsigned int >() << 1 << 2 );
|
||||
QCOMPARE( layerNames, QStringList() << QStringLiteral( "Layer 1" ) << QStringLiteral( "Layer 2" ) );
|
||||
QCOMPARE( mapLayerIds, QStringList() << QString() << QString() );
|
||||
layerIds.clear();
|
||||
layerNames.clear();
|
||||
mapLayerIds.clear();
|
||||
|
||||
QgsLayoutItemShape *shape = new QgsLayoutItemShape( &l );
|
||||
items << shape;
|
||||
res = exporter.handleLayeredExport( items, exportFunc );
|
||||
QCOMPARE( res, QgsLayoutExporter::Success );
|
||||
QCOMPARE( layerIds, QList< unsigned int >() << 1 << 2 );
|
||||
QCOMPARE( layerNames, QStringList() << QStringLiteral( "Layer 1" ) << QStringLiteral( "Layer 2" ) );
|
||||
QCOMPARE( mapLayerIds, QStringList() << QString() << QString() );
|
||||
layerIds.clear();
|
||||
layerNames.clear();
|
||||
mapLayerIds.clear();
|
||||
|
||||
QgsLayoutItemLabel *label2 = new QgsLayoutItemLabel( &l );
|
||||
items << label2;
|
||||
res = exporter.handleLayeredExport( items, exportFunc );
|
||||
QCOMPARE( res, QgsLayoutExporter::Success );
|
||||
QCOMPARE( layerIds, QList< unsigned int >() << 1 << 2 );
|
||||
QCOMPARE( layerNames, QStringList() << QStringLiteral( "Layer 1" ) << QStringLiteral( "Layer 2" ) );
|
||||
QCOMPARE( mapLayerIds, QStringList() << QString() << QString() );
|
||||
layerIds.clear();
|
||||
layerNames.clear();
|
||||
mapLayerIds.clear();
|
||||
|
||||
// add an item which can only be used with other similar items, should break the next label into a different layer
|
||||
QgsLayoutItemScaleBar *scaleBar = new QgsLayoutItemScaleBar( &l );
|
||||
items << scaleBar;
|
||||
res = exporter.handleLayeredExport( items, exportFunc );
|
||||
QCOMPARE( res, QgsLayoutExporter::Success );
|
||||
QCOMPARE( layerIds, QList< unsigned int >() << 1 << 2 << 3 );
|
||||
QCOMPARE( layerNames, QStringList() << QStringLiteral( "Layer 1" ) << QStringLiteral( "Layer 2" ) << QStringLiteral( "Layer 3" ) );
|
||||
QCOMPARE( mapLayerIds, QStringList() << QString() << QString() << QString() );
|
||||
layerIds.clear();
|
||||
layerNames.clear();
|
||||
mapLayerIds.clear();
|
||||
|
||||
QgsLayoutItemLabel *label3 = new QgsLayoutItemLabel( &l );
|
||||
items << label3;
|
||||
res = exporter.handleLayeredExport( items, exportFunc );
|
||||
QCOMPARE( res, QgsLayoutExporter::Success );
|
||||
QCOMPARE( layerIds, QList< unsigned int >() << 1 << 2 << 3 << 4 );
|
||||
QCOMPARE( layerNames, QStringList() << QStringLiteral( "Layer 1" ) << QStringLiteral( "Layer 2" ) << QStringLiteral( "Layer 3" ) << QStringLiteral( "Layer 4" ) );
|
||||
QCOMPARE( mapLayerIds, QStringList() << QString() << QString() << QString() << QString() );
|
||||
layerIds.clear();
|
||||
layerNames.clear();
|
||||
mapLayerIds.clear();
|
||||
|
||||
// multiple scalebars can be placed in the same layer
|
||||
QgsLayoutItemScaleBar *scaleBar2 = new QgsLayoutItemScaleBar( &l );
|
||||
items << scaleBar2;
|
||||
QgsLayoutItemScaleBar *scaleBar3 = new QgsLayoutItemScaleBar( &l );
|
||||
items << scaleBar3;
|
||||
res = exporter.handleLayeredExport( items, exportFunc );
|
||||
QCOMPARE( res, QgsLayoutExporter::Success );
|
||||
QCOMPARE( layerIds, QList< unsigned int >() << 1 << 2 << 3 << 4 << 5 );
|
||||
QCOMPARE( layerNames, QStringList() << QStringLiteral( "Layer 1" ) << QStringLiteral( "Layer 2" ) << QStringLiteral( "Layer 3" ) << QStringLiteral( "Layer 4" ) << QStringLiteral( "Layer 5" ) );
|
||||
QCOMPARE( mapLayerIds, QStringList() << QString() << QString() << QString() << QString() << QString() );
|
||||
layerIds.clear();
|
||||
layerNames.clear();
|
||||
mapLayerIds.clear();
|
||||
|
||||
// with an item which has sublayers
|
||||
QgsVectorLayer *linesLayer = new QgsVectorLayer( TEST_DATA_DIR + QStringLiteral( "/lines.shp" ),
|
||||
QStringLiteral( "lines" ), QStringLiteral( "ogr" ) );
|
||||
QVERIFY( linesLayer->isValid() );
|
||||
QgsVectorLayer *pointsLayer = new QgsVectorLayer( TEST_DATA_DIR + QStringLiteral( "/points.shp" ),
|
||||
QStringLiteral( "points" ), QStringLiteral( "ogr" ) );
|
||||
QVERIFY( pointsLayer->isValid() );
|
||||
|
||||
p.addMapLayer( linesLayer );
|
||||
p.addMapLayer( pointsLayer );
|
||||
|
||||
QgsLayoutItemMap *map = new QgsLayoutItemMap( &l );
|
||||
map->attemptSetSceneRect( QRectF( 20, 20, 200, 100 ) );
|
||||
map->setFrameEnabled( false );
|
||||
map->setBackgroundEnabled( false );
|
||||
map->setCrs( linesLayer->crs() );
|
||||
map->zoomToExtent( linesLayer->extent() );
|
||||
map->setLayers( QList<QgsMapLayer *>() << linesLayer );
|
||||
|
||||
items << map;
|
||||
res = exporter.handleLayeredExport( items, exportFunc );
|
||||
QCOMPARE( res, QgsLayoutExporter::Success );
|
||||
QCOMPARE( layerIds, QList< unsigned int >() << 1 << 2 << 3 << 4 << 5 << 6 );
|
||||
QCOMPARE( layerNames, QStringList() << QStringLiteral( "Layer 1" ) << QStringLiteral( "Layer 2" ) << QStringLiteral( "Layer 3" ) << QStringLiteral( "Layer 4" ) << QStringLiteral( "Layer 5" ) << QStringLiteral( "lines" ) );
|
||||
QCOMPARE( mapLayerIds, QStringList() << QString() << QString() << QString() << QString() << QString() << linesLayer->id() );
|
||||
layerIds.clear();
|
||||
layerNames.clear();
|
||||
mapLayerIds.clear();
|
||||
|
||||
map->setFrameEnabled( true );
|
||||
map->setBackgroundEnabled( true );
|
||||
res = exporter.handleLayeredExport( items, exportFunc );
|
||||
QCOMPARE( res, QgsLayoutExporter::Success );
|
||||
QCOMPARE( layerIds, QList< unsigned int >() << 1 << 2 << 3 << 4 << 5 << 6 << 7 << 8 );
|
||||
QCOMPARE( layerNames, QStringList() << QStringLiteral( "Layer 1" ) << QStringLiteral( "Layer 2" ) << QStringLiteral( "Layer 3" ) << QStringLiteral( "Layer 4" ) << QStringLiteral( "Layer 5" ) << QStringLiteral( "Map Background" ) << QStringLiteral( "lines" ) << QStringLiteral( "Map Frame" ) );
|
||||
QCOMPARE( mapLayerIds, QStringList() << QString() << QString() << QString() << QString() << QString() << QString() << linesLayer->id() << QString() );
|
||||
layerIds.clear();
|
||||
layerNames.clear();
|
||||
mapLayerIds.clear();
|
||||
|
||||
// add two legends -- legends are complex and must be placed in an isolated layer
|
||||
QgsLayoutItemLegend *legend = new QgsLayoutItemLegend( &l );
|
||||
QgsLayoutItemLegend *legend2 = new QgsLayoutItemLegend( &l );
|
||||
items << legend << legend2;
|
||||
res = exporter.handleLayeredExport( items, exportFunc );
|
||||
QCOMPARE( res, QgsLayoutExporter::Success );
|
||||
QCOMPARE( layerIds, QList< unsigned int >() << 1 << 2 << 3 << 4 << 5 << 6 << 7 << 8 << 9 << 10 );
|
||||
QCOMPARE( layerNames, QStringList() << QStringLiteral( "Layer 1" ) << QStringLiteral( "Layer 2" ) << QStringLiteral( "Layer 3" ) << QStringLiteral( "Layer 4" ) << QStringLiteral( "Layer 5" ) << QStringLiteral( "Map Background" ) << QStringLiteral( "lines" ) << QStringLiteral( "Map Frame" ) << QStringLiteral( "Layer 9" ) << QStringLiteral( "Layer 10" ) );
|
||||
QCOMPARE( mapLayerIds, QStringList() << QString() << QString() << QString() << QString() << QString() << QString() << linesLayer->id() << QString() << QString() << QString() );
|
||||
layerIds.clear();
|
||||
layerNames.clear();
|
||||
mapLayerIds.clear();
|
||||
|
||||
qDeleteAll( items );
|
||||
}
|
||||
|
||||
QGSTEST_MAIN( TestQgsLayoutExporter )
|
||||
#include "testqgslayoutexporter.moc"
|
Loading…
x
Reference in New Issue
Block a user