[layouts][needs-docs] Add control for whether pages should be

exported, including data defined setting

This replaces the 2.x data-defined "number of pages" setting.
Instead of requiring users to develop an expression to return
the number of pages, instead we allow individual pages to have
a data defined control of whether that page should be included
in the export.

This is more flexible, and works correctly with the mixed page
size model for layouts.
This commit is contained in:
Nyall Dawson 2017-12-15 15:10:11 +10:00
parent 3704c48bc0
commit f649f1f8a7
13 changed files with 204 additions and 38 deletions

View File

@ -580,9 +580,6 @@ Emitted when the layout's name is changed.
};
/************************************************************************
* This file has been generated automatically from *
* *

View File

@ -1511,6 +1511,9 @@ void QgsLayoutDesignerDialog::exportToRaster()
mView->setPaintingEnabled( false );
QApplication::setOverrideCursor( Qt::BusyCursor );
// force a refresh, to e.g. update data defined properties, tables, etc
mLayout->refresh();
QgsLayoutExporter exporter( mLayout );
QgsLayoutExporter::ImageExportSettings settings;
@ -1624,6 +1627,9 @@ void QgsLayoutDesignerDialog::exportToPdf()
pdfSettings.rasterizeWholeImage = mLayout->customProperty( QStringLiteral( "rasterise" ), false ).toBool();
pdfSettings.forceVectorOutput = mLayout->customProperty( QStringLiteral( "forceVector" ), false ).toBool();
// force a refresh, to e.g. update data defined properties, tables, etc
mLayout->refresh();
QgsLayoutExporter exporter( mLayout );
switch ( exporter.exportToPdf( outputFileName, pdfSettings ) )
{

View File

@ -39,6 +39,7 @@ QgsLayoutPagePropertiesWidget::QgsLayoutPagePropertiesWidget( QWidget *parent, Q
mWidthSpin->setValue( mPage->pageSize().width() );
mHeightSpin->setValue( mPage->pageSize().height() );
mSizeUnitsComboBox->setUnit( mPage->pageSize().units() );
mExcludePageCheckBox->setChecked( mPage->excludeFromExports() );
mPageOrientationComboBox->setCurrentIndex( mPageOrientationComboBox->findData( mPage->orientation() ) );
@ -59,11 +60,14 @@ QgsLayoutPagePropertiesWidget::QgsLayoutPagePropertiesWidget( QWidget *parent, Q
connect( mHeightSpin, static_cast< void ( QDoubleSpinBox::* )( double )>( &QDoubleSpinBox::valueChanged ), this, &QgsLayoutPagePropertiesWidget::updatePageSize );
connect( mWidthSpin, static_cast< void ( QDoubleSpinBox::* )( double )>( &QDoubleSpinBox::valueChanged ), this, &QgsLayoutPagePropertiesWidget::setToCustomSize );
connect( mHeightSpin, static_cast< void ( QDoubleSpinBox::* )( double )>( &QDoubleSpinBox::valueChanged ), this, &QgsLayoutPagePropertiesWidget::setToCustomSize );
connect( mExcludePageCheckBox, &QCheckBox::toggled, this, &QgsLayoutPagePropertiesWidget::excludeExportsToggled );
connect( mSymbolButton, &QgsSymbolButton::changed, this, &QgsLayoutPagePropertiesWidget::symbolChanged );
registerDataDefinedButton( mPaperSizeDDBtn, QgsLayoutObject::PresetPaperSize );
registerDataDefinedButton( mWidthDDBtn, QgsLayoutObject::ItemWidth );
registerDataDefinedButton( mHeightDDBtn, QgsLayoutObject::ItemHeight );
registerDataDefinedButton( mExcludePageDDBtn, QgsLayoutObject::ExcludeFromExports );
mExcludePageDDBtn->registerEnabledWidget( mExcludePageCheckBox, false );
showCurrentPageSize();
}
@ -155,6 +159,13 @@ void QgsLayoutPagePropertiesWidget::symbolChanged()
mPage->layout()->undoStack()->endCommand();
}
void QgsLayoutPagePropertiesWidget::excludeExportsToggled( bool checked )
{
mPage->beginCommand( !checked ? tr( "Include Page in Exports" ) : tr( "Exclude Page from Exports" ) );
mPage->setExcludeFromExports( checked );
mPage->endCommand();
}
void QgsLayoutPagePropertiesWidget::showCurrentPageSize()
{
QgsLayoutSize paperSize = mPage->pageSize();

View File

@ -48,6 +48,7 @@ class QgsLayoutPagePropertiesWidget : public QgsLayoutItemBaseWidget, private Ui
void updatePageSize();
void setToCustomSize();
void symbolChanged();
void excludeExportsToggled( bool checked );
private:

View File

@ -672,6 +672,3 @@ class CORE_EXPORT QgsLayout : public QGraphicsScene, public QgsExpressionContext
};
#endif //QGSLAYOUT_H

View File

@ -236,7 +236,8 @@ class CORE_EXPORT QgsLayoutContext : public QObject
bool mPagesVisible = true;
friend class QgsLayoutExporter;
friend class LayoutItemCacheSettingRestorer;
friend class TestQgsLayout;
friend class LayoutContextPreviewSettingRestorer;
};

View File

@ -26,6 +26,30 @@
#include "gdal.h"
#include "cpl_conv.h"
///@cond PRIVATE
class LayoutContextPreviewSettingRestorer
{
public:
LayoutContextPreviewSettingRestorer( QgsLayout *layout )
: mLayout( layout )
, mPreviousSetting( layout->context().mIsPreviewRender )
{
mLayout->context().mIsPreviewRender = false;
}
~LayoutContextPreviewSettingRestorer()
{
mLayout->context().mIsPreviewRender = mPreviousSetting;
}
private:
QgsLayout *mLayout = nullptr;
bool mPreviousSetting = false;
};
///@endcond PRIVATE
QgsLayoutExporter::QgsLayoutExporter( QgsLayout *layout )
: mLayout( layout )
{
@ -53,6 +77,9 @@ void QgsLayoutExporter::renderPage( QPainter *painter, int page ) const
return;
}
LayoutContextPreviewSettingRestorer restorer( mLayout );
( void )restorer;
QRectF paperRect = QRectF( pageItem->pos().x(), pageItem->pos().y(), pageItem->rect().width(), pageItem->rect().height() );
renderRegion( painter, paperRect );
}
@ -73,6 +100,9 @@ QImage QgsLayoutExporter::renderPageToImage( int page, QSize imageSize, double d
return QImage();
}
LayoutContextPreviewSettingRestorer restorer( mLayout );
( void )restorer;
QRectF paperRect = QRectF( pageItem->pos().x(), pageItem->pos().y(), pageItem->rect().width(), pageItem->rect().height() );
return renderRegionToImage( paperRect, imageSize, dpi );
}
@ -85,8 +115,6 @@ class LayoutItemCacheSettingRestorer
LayoutItemCacheSettingRestorer( QgsLayout *layout )
: mLayout( layout )
{
mLayout->context().mIsPreviewRender = false;
const QList< QGraphicsItem * > items = mLayout->items();
for ( QGraphicsItem *item : items )
{
@ -101,8 +129,6 @@ class LayoutItemCacheSettingRestorer
{
it.key()->setCacheMode( it.value() );
}
mLayout->context().mIsPreviewRender = true;
}
private:
@ -122,6 +148,8 @@ void QgsLayoutExporter::renderRegion( QPainter *painter, const QRectF &region )
LayoutItemCacheSettingRestorer cacheRestorer( mLayout );
( void )cacheRestorer;
LayoutContextPreviewSettingRestorer restorer( mLayout );
( void )restorer;
#if 0 //TODO
setSnapLinesVisible( false );
@ -138,6 +166,9 @@ void QgsLayoutExporter::renderRegion( QPainter *painter, const QRectF &region )
QImage QgsLayoutExporter::renderRegionToImage( const QRectF &region, QSize imageSize, double dpi ) const
{
LayoutContextPreviewSettingRestorer restorer( mLayout );
( void )restorer;
double resolution = mLayout->context().dpi();
double oneInchInLayoutUnits = mLayout->convertToLayoutUnits( QgsLayoutMeasurement( 1, QgsUnitTypes::LayoutInches ) );
if ( imageSize.isValid() )
@ -219,6 +250,8 @@ QgsLayoutExporter::ExportResult QgsLayoutExporter::exportToImage( const QString
pageDetails.baseName = fi.baseName();
pageDetails.extension = fi.completeSuffix();
LayoutContextPreviewSettingRestorer restorer( mLayout );
( void )restorer;
LayoutContextSettingsRestorer dpiRestorer( mLayout );
( void )dpiRestorer;
mLayout->context().setDpi( settings.dpi );
@ -303,6 +336,8 @@ QgsLayoutExporter::ExportResult QgsLayoutExporter::exportToPdf( const QString &f
mErrorFileName.clear();
LayoutContextPreviewSettingRestorer restorer( mLayout );
( void )restorer;
LayoutContextSettingsRestorer contextRestorer( mLayout );
( void )contextRestorer;
mLayout->context().setDpi( settings.dpi );

View File

@ -831,6 +831,11 @@ class CORE_EXPORT QgsLayoutItem : public QgsLayoutObject, public QGraphicsRectIt
*/
void cancelCommand();
/**
* Returns whether the item should be drawn in the current context.
*/
bool shouldDrawItem() const;
public slots:
/**
@ -1049,11 +1054,6 @@ class CORE_EXPORT QgsLayoutItem : public QgsLayoutObject, public QGraphicsRectIt
*/
virtual bool readPropertiesFromElement( const QDomElement &element, const QDomDocument &document, const QgsReadWriteContext &context );
/**
* Returns whether the item should be drawn in the current context.
*/
bool shouldDrawItem() const;
/**
* Applies any present data defined size overrides to the specified layout \a size.
*/

View File

@ -482,6 +482,10 @@ bool QgsLayoutPageCollection::shouldExportPage( int page ) const
return false;
}
QgsLayoutItemPage *pageItem = mPages.at( page );
if ( !pageItem->shouldDrawItem() )
return false;
//check all frame items on page
QList<QgsLayoutFrame *> frames;
itemsOnPage( frames, page );

View File

@ -14,27 +14,7 @@
<string>New Item Properties</string>
</property>
<layout class="QGridLayout" name="gridLayout_2" columnstretch="0,1">
<item row="3" column="0">
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label">
<property name="text">
<string>Background</string>
</property>
</widget>
</item>
<item row="2" column="1">
<item row="4" column="1">
<widget class="QgsSymbolButton" name="mSymbolButton">
<property name="enabled">
<bool>true</bool>
@ -56,6 +36,13 @@
</property>
</widget>
</item>
<item row="4" column="0">
<widget class="QLabel" name="label">
<property name="text">
<string>Background</string>
</property>
</widget>
</item>
<item row="0" column="0" colspan="2">
<widget class="QGroupBox" name="groupBox">
<property name="title">
@ -204,6 +191,56 @@
</layout>
</widget>
</item>
<item row="5" column="0">
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
<item row="1" column="0" colspan="2">
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<widget class="QCheckBox" name="mExcludePageCheckBox">
<property name="toolTip">
<string>If checked, this page will not be included when exporting the layout</string>
</property>
<property name="text">
<string>Exclude page from exports</string>
</property>
<property name="checked">
<bool>false</bool>
</property>
</widget>
</item>
<item>
<widget class="QgsPropertyOverrideButton" name="mExcludePageDDBtn">
<property name="text">
<string>…</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
</layout>
</widget>
<customwidgets>
@ -244,6 +281,9 @@
<tabstop>mHeightSpin</tabstop>
<tabstop>mHeightDDBtn</tabstop>
<tabstop>mSizeUnitsComboBox</tabstop>
<tabstop>mExcludePageCheckBox</tabstop>
<tabstop>mExcludePageDDBtn</tabstop>
<tabstop>mSymbolButton</tabstop>
</tabstops>
<resources/>
<connections/>

View File

@ -17,7 +17,7 @@
<x>0</x>
<y>0</y>
<width>1018</width>
<height>20</height>
<height>25</height>
</rect>
</property>
<property name="toolTip">

View File

@ -613,6 +613,7 @@ void TestQgsLayout::shouldExportPage()
QgsLayoutItemPage *page2 = new QgsLayoutItemPage( &l );
page2->setPageSize( "A4" );
l.pageCollection()->addPage( page2 );
l.context().mIsPreviewRender = false;
QgsLayoutItemHtml *htmlItem = new QgsLayoutItemHtml( &l );
//frame on page 1
@ -645,6 +646,33 @@ void TestQgsLayout::shouldExportPage()
QVERIFY( l.pageCollection()->shouldExportPage( 0 ) );
QVERIFY( !l.pageCollection()->shouldExportPage( 1 ) );
// get rid of frames
l.removeItem( frame1 );
l.removeItem( frame2 );
l.removeMultiFrame( htmlItem );
delete htmlItem;
QgsApplication::sendPostedEvents( nullptr, QEvent::DeferredDelete );
QVERIFY( l.pageCollection()->shouldExportPage( 0 ) );
QVERIFY( l.pageCollection()->shouldExportPage( 1 ) );
// explicitly set exclude from exports
l.pageCollection()->page( 0 )->setExcludeFromExports( true );
QVERIFY( !l.pageCollection()->shouldExportPage( 0 ) );
QVERIFY( l.pageCollection()->shouldExportPage( 1 ) );
l.pageCollection()->page( 0 )->setExcludeFromExports( false );
l.pageCollection()->page( 1 )->setExcludeFromExports( true );
QVERIFY( l.pageCollection()->shouldExportPage( 0 ) );
QVERIFY( !l.pageCollection()->shouldExportPage( 1 ) );
l.pageCollection()->page( 1 )->setExcludeFromExports( false );
l.pageCollection()->page( 0 )->dataDefinedProperties().setProperty( QgsLayoutObject::ExcludeFromExports, QgsProperty::fromExpression( "1" ) );
l.pageCollection()->page( 1 )->dataDefinedProperties().setProperty( QgsLayoutObject::ExcludeFromExports, QgsProperty::fromValue( true ) );
l.refresh();
QVERIFY( !l.pageCollection()->shouldExportPage( 0 ) );
QVERIFY( !l.pageCollection()->shouldExportPage( 1 ) );
}
void TestQgsLayout::pageIsEmpty()

View File

@ -317,6 +317,52 @@ class TestQgsLayoutExporter(unittest.TestCase):
self.assertAlmostEqual(values[4], 1925.000000000000, 2)
self.assertAlmostEqual(values[5], 3050.000000000000, 2)
def testExcludePagesImage(self):
l = QgsLayout(QgsProject.instance())
l.initializeDefaults()
# add a second page
page2 = QgsLayoutItemPage(l)
page2.setPageSize('A5')
l.pageCollection().addPage(page2)
exporter = QgsLayoutExporter(l)
# setup settings
settings = QgsLayoutExporter.ImageExportSettings()
settings.dpi = 80
settings.generateWorldFile = False
rendered_file_path = os.path.join(self.basetestpath, 'test_exclude_export.png')
details = QgsLayoutExporter.PageExportDetails()
details.directory = self.basetestpath
details.baseName = 'test_exclude_export'
details.extension = 'png'
details.page = 0
self.assertEqual(exporter.exportToImage(rendered_file_path, settings), QgsLayoutExporter.Success)
self.assertTrue(os.path.exists(exporter.generateFileName(details)))
details.page = 1
self.assertTrue(os.path.exists(exporter.generateFileName(details)))
# exclude a page
l.pageCollection().page(0).setExcludeFromExports(True)
rendered_file_path = os.path.join(self.basetestpath, 'test_exclude_export_excluded.png')
details.baseName = 'test_exclude_export_excluded'
details.page = 0
self.assertEqual(exporter.exportToImage(rendered_file_path, settings), QgsLayoutExporter.Success)
self.assertFalse(os.path.exists(exporter.generateFileName(details)))
details.page = 1
self.assertTrue(os.path.exists(exporter.generateFileName(details)))
# exclude second page
l.pageCollection().page(1).setExcludeFromExports(True)
rendered_file_path = os.path.join(self.basetestpath, 'test_exclude_export_excluded_all.png')
details.baseName = 'test_exclude_export_excluded_all'
details.page = 0
self.assertEqual(exporter.exportToImage(rendered_file_path, settings), QgsLayoutExporter.Success)
self.assertFalse(os.path.exists(exporter.generateFileName(details)))
details.page = 1
self.assertFalse(os.path.exists(exporter.generateFileName(details)))
def testPageFileName(self):
l = QgsLayout(QgsProject.instance())
exporter = QgsLayoutExporter(l)