Skip to content
Permalink
Browse files

[FEATURE][composer] Add checkbox to frame items for preventing export

of page containing frame when frame is empty. This change allows
users to create multiple pages containing extra frames for multiframe
items (currently HTML and Attribute Table items), which are then only
printed/exported if required. Sponsored by the City of Uster, Switzerland.
  • Loading branch information
nyalldawson committed Sep 25, 2014
1 parent b8bb4f0 commit 272b79b6ca49686cdc8775e00a215ed1d38e4218
@@ -41,5 +41,26 @@ class QgsComposerFrame: QgsComposerItem
* @see setContentSection
*/
QRectF extent() const;

/**Returns whether the page should be hidden (ie, not included in composer exports) if this frame is empty
* @returns true if page should be hidden if frame is empty
* @note added in QGIS 2.5
* @see setHidePageIfEmpty
*/
bool hidePageIfEmpty() const;

/**Sets whether the page should be hidden (ie, not included in composer exports) if this frame is empty
* @param hidePageIfEmpty set to true if page should be hidden if frame is empty
* @note added in QGIS 2.5
* @see hidePageIfEmpty
*/
void setHidePageIfEmpty( const bool hidePageIfEmpty );

/**Returns whether the frame is empty
* @returns true if frame is empty
* @note added in QGIS 2.5
* @see hidePageIfEmpty
*/
bool isEmpty() const;

};
@@ -56,22 +56,54 @@ class QgsComposition : QGraphicsScene

/**Changes size of paper item. Also moves all items so that they retain
* their same relative position to the top left corner of their current page.
* @param width page width in mm
* @param height page height in mm
* @see paperHeight
* @see paperWidth
*/
void setPaperSize( const double width, const double height );

/**Returns height of paper item*/
/**Height of paper item
* @returns height in mm
* @see paperWidth
* @see setPaperSize
*/
double paperHeight() const;

/**Returns width of paper item*/
/**Width of paper item
* @returns width in mm
* @see paperHeight
* @see setPaperSize
*/
double paperWidth() const;

/**Returns the vertical space between pages in a composer view
* @returns space between pages in mm
*/
double spaceBetweenPages() const;

/**Note: added in version 1.9*/
/**Sets the number of pages for the composition.
* @param pages number of pages
* @note added in version 1.9
* @see numPages
*/
void setNumPages( const int pages );
/**Note: added in version 1.9*/

/**Returns the number of pages in the composition.
* @returns number of pages
* @note added in version 1.9
* @see setNumPages
*/
int numPages() const;

/**Returns whether a specified page number should be included in exports of the composition.
* @param page page number, starting with 1
* @returns true if page should be exported
* @note added in QGIS 2.5
* @see numPages
*/
bool shouldExportPage( const int page ) const;

/**Note: added in version 2.1*/
void setPageStyleSymbol( QgsFillSymbolV2* symbol /Transfer/ );
/**Note: added in version 2.1*/
@@ -220,6 +252,14 @@ class QgsComposition : QGraphicsScene
@note not available in python bindings
*/
// template<class T> void composerItems( QList<T*>& itemList );

/**Return composer items of a specific type on a specified page
* @param itemList list of item type to store matching items in
* @param pageNumber page number (0 based)
* @note not available in python bindings
* @note added in QGIS 2.5
*/
//template<class T> void composerItemsOnPage( QList<T*>& itemList, const int pageNumber );

/**Returns the composer map with specified id
@return QgsComposerMap or 0 pointer if the composer map item does not exist*/
@@ -1739,6 +1739,10 @@ void QgsComposer::exportCompositionAsImage( QgsComposer::OutputMode mode )

for ( int i = 0; i < mComposition->numPages(); ++i )
{
if ( !mComposition->shouldExportPage( i + 1 ) )
{
continue;
}
QImage image = mComposition->printPageAsRaster( i );
if ( image.isNull() )
{
@@ -1912,6 +1916,10 @@ void QgsComposer::exportCompositionAsImage( QgsComposer::OutputMode mode )

for ( int i = 0; i < mComposition->numPages(); ++i )
{
if ( !mComposition->shouldExportPage( i + 1 ) )
{
continue;
}
QImage image = mComposition->printPageAsRaster( i );
QString imageFilename = filename;

@@ -2187,6 +2195,10 @@ void QgsComposer::exportCompositionAsSVG( QgsComposer::OutputMode mode )
{
for ( int i = 0; i < mComposition->numPages(); ++i )
{
if ( !mComposition->shouldExportPage( i + 1 ) )
{
continue;
}
QSvgGenerator generator;
generator.setTitle( QgsProject::instance()->title() );
QString currentFileName = outputFileName;
@@ -2234,6 +2246,10 @@ void QgsComposer::exportCompositionAsSVG( QgsComposer::OutputMode mode )

for ( int i = 0; i < mComposition->numPages(); ++i )
{
if ( !mComposition->shouldExportPage( i + 1 ) )
{
continue;
}
QDomDocument svg;
QDomNode svgDocRoot;
QgsPaperItem * paperItem = paperItems[i];
@@ -37,9 +37,6 @@ QgsComposerAttributeTableWidget::QgsComposerAttributeTableWidget( QgsComposerAtt
, mFrame( frame )
{
setupUi( this );
//add widget for general composer item properties
QgsComposerItemWidget* itemPropertiesWidget = new QgsComposerItemWidget( this, mFrame );
mainLayout->addWidget( itemPropertiesWidget );

blockAllSignals( true );

@@ -90,6 +87,14 @@ QgsComposerAttributeTableWidget::QgsComposerAttributeTableWidget( QgsComposerAtt
connect( atlas, SIGNAL( toggled( bool ) ), this, SLOT( atlasToggled() ) );
}
}

//embed widget for general options
if ( mFrame )
{
//add widget for general composer item properties
QgsComposerItemWidget* itemPropertiesWidget = new QgsComposerItemWidget( this, mFrame );
mainLayout->addWidget( itemPropertiesWidget );
}
}

QgsComposerAttributeTableWidget::~QgsComposerAttributeTableWidget()
@@ -509,6 +514,8 @@ void QgsComposerAttributeTableWidget::updateGuiElements()
mResizeModeComboBox->setCurrentIndex( mResizeModeComboBox->findData( mComposerTable->resizeMode() ) );
mAddFramePushButton->setEnabled( mComposerTable->resizeMode() == QgsComposerMultiFrame::UseExistingFrames );

mEmptyFrameCheckBox->setChecked( mFrame->hidePageIfEmpty() );

toggleSourceControls();

blockAllSignals( false );
@@ -539,7 +546,7 @@ void QgsComposerAttributeTableWidget::updateRelationsCombo()
if ( atlasLayer )
{
QList<QgsRelation> relations = QgsProject::instance()->relationManager()->referencedRelations( mComposerTable->composition()->atlasComposition().coverageLayer() );
Q_FOREACH( const QgsRelation& relation, relations )
Q_FOREACH ( const QgsRelation& relation, relations )
{
mRelationsComboBox->addItem( relation.name(), relation.id() );
}
@@ -608,6 +615,7 @@ void QgsComposerAttributeTableWidget::blockAllSignals( bool b )
mRelationsComboBox->blockSignals( b );
mEmptyModeComboBox->blockSignals( b );
mEmptyMessageLineEdit->blockSignals( b );
mEmptyFrameCheckBox->blockSignals( b );
}

void QgsComposerAttributeTableWidget::setMaximumNumberOfFeatures( int n )
@@ -662,6 +670,19 @@ void QgsComposerAttributeTableWidget::on_mUniqueOnlyCheckBox_stateChanged( int s
}
}


void QgsComposerAttributeTableWidget::on_mEmptyFrameCheckBox_toggled( bool checked )
{
if ( !mFrame )
{
return;
}

mFrame->beginCommand( tr( "Empty frame mode toggled" ) );
mFrame->setHidePageIfEmpty( checked );
mFrame->endCommand();
}

void QgsComposerAttributeTableWidget::on_mIntersectAtlasCheckBox_stateChanged( int state )
{
if ( !mComposerTable )
@@ -74,6 +74,7 @@ class QgsComposerAttributeTableWidget: public QgsComposerItemBaseWidget, private
void on_mEmptyMessageLineEdit_editingFinished();
void on_mIntersectAtlasCheckBox_stateChanged( int state );
void on_mUniqueOnlyCheckBox_stateChanged( int state );
void on_mEmptyFrameCheckBox_toggled( bool checked );

/**Inserts a new maximum number of features into the spin box (without the spinbox emitting a signal)*/
void setMaximumNumberOfFeatures( int n );
@@ -100,6 +100,7 @@ void QgsComposerHtmlWidget::blockSignals( bool block )
mRadioManualSource->blockSignals( block );
mRadioUrlSource->blockSignals( block );
mEvaluateExpressionsCheckbox->blockSignals( block );
mEmptyFrameCheckBox->blockSignals( block );
}

void QgsComposerHtmlWidget::on_mUrlLineEdit_editingFinished()
@@ -265,6 +266,18 @@ void QgsComposerHtmlWidget::on_mUserStylesheetCheckBox_toggled( bool checked )
}
}

void QgsComposerHtmlWidget::on_mEmptyFrameCheckBox_toggled( bool checked )
{
if ( !mFrame )
{
return;
}

mFrame->beginCommand( tr( "Empty frame mode toggled" ) );
mFrame->setHidePageIfEmpty( checked );
mFrame->endCommand();
}

void QgsComposerHtmlWidget::on_mRadioManualSource_clicked( bool checked )
{
if ( !mHtml )
@@ -433,6 +446,8 @@ void QgsComposerHtmlWidget::setGuiElementValues()
mUserStylesheetCheckBox->setChecked( mHtml->userStylesheetEnabled() );
mStylesheetEditor->setText( mHtml->userStylesheet() );

mEmptyFrameCheckBox->setChecked( mFrame->hidePageIfEmpty() );

populateDataDefinedButtons();

blockSignals( false );
@@ -47,6 +47,7 @@ class QgsComposerHtmlWidget: public QgsComposerItemBaseWidget, private Ui::QgsCo
void on_mReloadPushButton_clicked();
void on_mReloadPushButton2_clicked();
void on_mAddFramePushButton_clicked();
void on_mEmptyFrameCheckBox_toggled( bool checked );

/**Sets the GUI elements to the values of mHtmlItem*/
void setGuiElementValues();
@@ -780,7 +780,6 @@ void QgsComposerAttributeTableV2::addFrame( QgsComposerFrame *frame, bool recalc

if ( recalcFrameSizes )
{

recalculateFrameSizes();
}
}
@@ -20,6 +20,7 @@
QgsComposerFrame::QgsComposerFrame( QgsComposition* c, QgsComposerMultiFrame* mf, qreal x, qreal y, qreal width, qreal height )
: QgsComposerItem( x, y, width, height, c )
, mMultiFrame( mf )
, mHidePageIfEmpty( false )
{
//repaint frame when multiframe content changes
connect( mf, SIGNAL( contentsChanged() ), this, SLOT( repaint() ) );
@@ -33,6 +34,7 @@ QgsComposerFrame::QgsComposerFrame( QgsComposition* c, QgsComposerMultiFrame* mf
QgsComposerFrame::QgsComposerFrame()
: QgsComposerItem( 0, 0, 0, 0, 0 )
, mMultiFrame( 0 )
, mHidePageIfEmpty( false )
{
}

@@ -47,6 +49,8 @@ bool QgsComposerFrame::writeXML( QDomElement& elem, QDomDocument & doc ) const
frameElem.setAttribute( "sectionY", QString::number( mSection.y() ) );
frameElem.setAttribute( "sectionWidth", QString::number( mSection.width() ) );
frameElem.setAttribute( "sectionHeight", QString::number( mSection.height() ) );
frameElem.setAttribute( "hidePageIfEmpty", mHidePageIfEmpty );

elem.appendChild( frameElem );

return _writeXML( frameElem, doc );
@@ -59,6 +63,7 @@ bool QgsComposerFrame::readXML( const QDomElement& itemElem, const QDomDocument&
double width = itemElem.attribute( "sectionWidth" ).toDouble();
double height = itemElem.attribute( "sectionHeight" ).toDouble();
mSection = QRectF( x, y, width, height );
mHidePageIfEmpty = itemElem.attribute( "hidePageIfEmpty", "0" ).toInt();
QDomElement composerItem = itemElem.firstChildElement( "ComposerItem" );
if ( composerItem.isNull() )
{
@@ -67,6 +72,29 @@ bool QgsComposerFrame::readXML( const QDomElement& itemElem, const QDomDocument&
return _readXML( composerItem, doc );
}

void QgsComposerFrame::setHidePageIfEmpty( const bool hidePageIfEmpty )
{
mHidePageIfEmpty = hidePageIfEmpty;
}

bool QgsComposerFrame::isEmpty() const
{
if ( !mMultiFrame )
{
return true;
}

double multiFrameHeight = mMultiFrame->totalSize().height();
if ( multiFrameHeight <= mSection.top() )
{
//multiframe height is less than top of this frame's visible portion
return true;
}

return false;

}

QString QgsComposerFrame::displayName() const
{
if ( !id().isEmpty() )
@@ -64,10 +64,34 @@ class CORE_EXPORT QgsComposerFrame: public QgsComposerItem
*/
QRectF extent() const { return mSection; }

/**Returns whether the page should be hidden (ie, not included in composer exports) if this frame is empty
* @returns true if page should be hidden if frame is empty
* @note added in QGIS 2.5
* @see setHidePageIfEmpty
*/
bool hidePageIfEmpty() const { return mHidePageIfEmpty; }

/**Sets whether the page should be hidden (ie, not included in composer exports) if this frame is empty
* @param hidePageIfEmpty set to true if page should be hidden if frame is empty
* @note added in QGIS 2.5
* @see hidePageIfEmpty
*/
void setHidePageIfEmpty( const bool hidePageIfEmpty );

/**Returns whether the frame is empty
* @returns true if frame is empty
* @note added in QGIS 2.5
* @see hidePageIfEmpty
*/
bool isEmpty() const;

private:
QgsComposerFrame(); //forbidden
QgsComposerMultiFrame* mMultiFrame;
QRectF mSection;

/**if true, composition will not export page if this frame is empty*/
bool mHidePageIfEmpty;
};

#endif // QGSCOMPOSERFRAME_H
@@ -173,6 +173,10 @@ void QgsComposerHtml::loadHtml()
}

mLoaded = false;

//reset page size. otherwise viewport size increases but never decreases again
mWebPage->setViewportSize( QSize( 0, 0 ) );

//set html, using the specified url as base if in Url mode
mWebPage->mainFrame()->setHtml( loadedHtml, mContentMode == QgsComposerHtml::Url ? QUrl( mActualFetchedUrl ) : QUrl() );

@@ -684,8 +684,10 @@ double QgsComposerTableV2::totalHeight() const
double heightOfLastFrame = 0;
for ( int idx = 0; idx < numberExistingFrames; ++idx )
{
rowsVisibleInLastFrame = rowsVisible( idx );
bool hasHeader = (( mHeaderMode == QgsComposerTableV2::FirstFrame && idx == 0 )
|| ( mHeaderMode == QgsComposerTableV2::AllFrames ) );
heightOfLastFrame = frame( idx )->rect().height();
rowsVisibleInLastFrame = rowsVisible( heightOfLastFrame, hasHeader );
rowsAlreadyShown += rowsVisibleInLastFrame;
height += heightOfLastFrame;
if ( rowsAlreadyShown >= mTableContents.length() )

0 comments on commit 272b79b

Please sign in to comment.
You can’t perform that action at this time.