Skip to content

Commit

Permalink
[composer] Add a checkbox for legends to prevent automatic resizing
Browse files Browse the repository at this point in the history
A new checkbox has been added to the legend settings to control
whether or not a legend should be automatically resized to fit
its contents.

If unchecked, then the legend will never resize and instead just
stick to whatever size the user has set. Any content which
doesn't fit the size is cropped out.

Refs #10556

On behalf of Faunalia, sponsored by ENEL

(cherry-picked from 2f8c6f5)
  • Loading branch information
nyalldawson committed Jul 19, 2016
1 parent b919342 commit a52f31a
Show file tree
Hide file tree
Showing 11 changed files with 190 additions and 21 deletions.
14 changes: 14 additions & 0 deletions python/core/composer/qgscomposerlegend.sip
Expand Up @@ -46,6 +46,20 @@ class QgsComposerLegend : QgsComposerItem
/** Sets item box to the whole content*/
void adjustBoxSize();

/** Sets whether the legend should automatically resize to fit its contents.
* @param enabled set to false to disable automatic resizing. The legend frame will not
* be expanded to fit legend items, and items may be cropped from display.
* @see resizeToContents()
* @note added in QGIS 3.0
*/
void setResizeToContents( bool enabled );

/** Returns whether the legend should automatically resize to fit its contents.
* @see setResizeToContents()
* @note added in QGIS 3.0
*/
bool resizeToContents() const;

/** Returns pointer to the legend model*/
//! @deprecated in 2.6 - use modelV2()
QgsLegendModel* model() /Deprecated/;
Expand Down
18 changes: 18 additions & 0 deletions src/app/composer/qgscomposerlegendwidget.cpp
Expand Up @@ -137,6 +137,8 @@ void QgsComposerLegendWidget::setGuiElements()

mCheckBoxAutoUpdate->setChecked( mLegend->autoUpdateModel() );

mCheckboxResizeContents->setChecked( mLegend->resizeToContents() );

const QgsComposerMap* map = mLegend->composerMap();
mMapComboBox->setItem( map );
mFontColorButton->setColor( mLegend->fontColor() );
Expand Down Expand Up @@ -572,6 +574,21 @@ void QgsComposerLegendWidget::composerMapChanged( QgsComposerItem* item )
}
}

void QgsComposerLegendWidget::on_mCheckboxResizeContents_toggled( bool checked )
{
if ( !mLegend )
{
return;
}

mLegend->beginCommand( tr( "Legend resize to contents" ) );
mLegend->setResizeToContents( checked );
if ( checked )
mLegend->adjustBoxSize();
mLegend->updateItem();
mLegend->endCommand();
}

void QgsComposerLegendWidget::on_mRasterBorderGroupBox_toggled( bool state )
{
if ( !mLegend )
Expand Down Expand Up @@ -886,6 +903,7 @@ void QgsComposerLegendWidget::blockAllSignals( bool b )
mRasterBorderWidthSpinBox->blockSignals( b );
mWmsLegendWidthSpinBox->blockSignals( b );
mWmsLegendHeightSpinBox->blockSignals( b );
mCheckboxResizeContents->blockSignals( b );
mTitleSpaceBottomSpinBox->blockSignals( b );
}

Expand Down
1 change: 1 addition & 0 deletions src/app/composer/qgscomposerlegendwidget.h
Expand Up @@ -68,6 +68,7 @@ class QgsComposerLegendWidget: public QgsComposerItemBaseWidget, private Ui::Qgs
void on_mColumnSpaceSpinBox_valueChanged( double d );
void on_mCheckBoxAutoUpdate_stateChanged( int state );
void composerMapChanged( QgsComposerItem* item );
void on_mCheckboxResizeContents_toggled( bool checked );

void on_mRasterBorderGroupBox_toggled( bool state );
void on_mRasterBorderWidthSpinBox_valueChanged( double d );
Expand Down
66 changes: 47 additions & 19 deletions src/core/composer/qgscomposerlegend.cpp
Expand Up @@ -44,6 +44,7 @@ QgsComposerLegend::QgsComposerLegend( QgsComposition* composition )
, mInAtlas( false )
, mInitialMapScaleCalculated( false )
, mForceResize( false )
, mSizeToContents( true )
{
mLegendModel2 = new QgsLegendModelV2( QgsProject::instance()->layerTreeRoot() );

Expand All @@ -69,6 +70,7 @@ QgsComposerLegend::QgsComposerLegend()
, mInAtlas( false )
, mInitialMapScaleCalculated( false )
, mForceResize( false )
, mSizeToContents( true )
{

}
Expand Down Expand Up @@ -120,28 +122,31 @@ void QgsComposerLegend::paint( QPainter* painter, const QStyleOptionGraphicsItem
mInitialMapScaleCalculated = true;

QgsLegendRenderer legendRenderer( mLegendModel2, mSettings );
legendRenderer.setLegendSize( mForceResize ? QSize() : rect().size() );
legendRenderer.setLegendSize( mForceResize && mSizeToContents ? QSize() : rect().size() );

//adjust box if width or height is too small
QSizeF size = legendRenderer.minimumSize();
if ( mForceResize )
if ( mSizeToContents )
{
mForceResize = false;
//set new rect, respecting position mode and data defined size/position
QRectF targetRect = QRectF( pos().x(), pos().y(), size.width(), size.height() );
setSceneRect( evalItemRect( targetRect, true ) );
}
else if ( size.height() > rect().height() || size.width() > rect().width() )
{
//need to resize box
QRectF targetRect = QRectF( pos().x(), pos().y(), rect().width(), rect().height() );
if ( size.height() > targetRect.height() )
targetRect.setHeight( size.height() );
if ( size.width() > rect().width() )
targetRect.setWidth( size.width() );

//set new rect, respecting position mode and data defined size/position
setSceneRect( evalItemRect( targetRect, true ) );
QSizeF size = legendRenderer.minimumSize();
if ( mForceResize )
{
mForceResize = false;
//set new rect, respecting position mode and data defined size/position
QRectF targetRect = QRectF( pos().x(), pos().y(), size.width(), size.height() );
setSceneRect( evalItemRect( targetRect, true ) );
}
else if ( size.height() > rect().height() || size.width() > rect().width() )
{
//need to resize box
QRectF targetRect = QRectF( pos().x(), pos().y(), rect().width(), rect().height() );
if ( size.height() > targetRect.height() )
targetRect.setHeight( size.height() );
if ( size.width() > rect().width() )
targetRect.setWidth( size.width() );

//set new rect, respecting position mode and data defined size/position
setSceneRect( evalItemRect( targetRect, true ) );
}
}

drawBackground( painter );
Expand All @@ -150,6 +155,13 @@ void QgsComposerLegend::paint( QPainter* painter, const QStyleOptionGraphicsItem
painter->setRenderHint( QPainter::Antialiasing, true );
painter->setPen( QPen( QColor( 0, 0, 0 ) ) );

if ( !mSizeToContents )
{
// set a clip region to crop out parts of legend which don't fit
QRectF thisPaintRect = QRectF( 0, 0, rect().width(), rect().height() );
painter->setClipRect( thisPaintRect );
}

legendRenderer.drawLegend( painter );

painter->restore();
Expand Down Expand Up @@ -180,6 +192,9 @@ QSizeF QgsComposerLegend::paintAndDetermineSize( QPainter* painter )

void QgsComposerLegend::adjustBoxSize()
{
if ( !mSizeToContents )
return;

if ( !mInitialMapScaleCalculated )
{
// this is messy - but until we have painted the item we have no knowledge of the current DPI
Expand All @@ -200,6 +215,15 @@ void QgsComposerLegend::adjustBoxSize()
}
}

void QgsComposerLegend::setResizeToContents( bool enabled )
{
mSizeToContents = enabled;
}

bool QgsComposerLegend::resizeToContents() const
{
return mSizeToContents;
}

void QgsComposerLegend::setCustomLayerTree( QgsLayerTreeGroup* rootGroup )
{
Expand Down Expand Up @@ -362,6 +386,8 @@ bool QgsComposerLegend::writeXML( QDomElement& elem, QDomDocument & doc ) const
composerLegendElem.setAttribute( "wrapChar", mSettings.wrapChar() );
composerLegendElem.setAttribute( "fontColor", mSettings.fontColor().name() );

composerLegendElem.setAttribute( "resizeToContents", mSizeToContents );

if ( mComposerMap )
{
composerLegendElem.setAttribute( "map", mComposerMap->id() );
Expand Down Expand Up @@ -488,6 +514,8 @@ bool QgsComposerLegend::readXML( const QDomElement& itemElem, const QDomDocument

mSettings.setWrapChar( itemElem.attribute( "wrapChar" ) );

mSizeToContents = itemElem.attribute( "resizeToContents", "1" ) != "0";

//composer map
mLegendFilterByMap = itemElem.attribute( "legendFilterByMap", "0" ).toInt();
if ( !itemElem.attribute( "map" ).isEmpty() )
Expand Down
17 changes: 17 additions & 0 deletions src/core/composer/qgscomposerlegend.h
Expand Up @@ -75,6 +75,20 @@ class CORE_EXPORT QgsComposerLegend : public QgsComposerItem
/** Sets item box to the whole content*/
void adjustBoxSize();

/** Sets whether the legend should automatically resize to fit its contents.
* @param enabled set to false to disable automatic resizing. The legend frame will not
* be expanded to fit legend items, and items may be cropped from display.
* @see resizeToContents()
* @note added in QGIS 3.0
*/
void setResizeToContents( bool enabled );

/** Returns whether the legend should automatically resize to fit its contents.
* @see setResizeToContents()
* @note added in QGIS 3.0
*/
bool resizeToContents() const;

/** Returns pointer to the legend model*/
//! @deprecated in 2.6 - use modelV2()
Q_DECL_DEPRECATED QgsLegendModel* model() {return &mLegendModel;}
Expand Down Expand Up @@ -299,6 +313,9 @@ class CORE_EXPORT QgsComposerLegend : public QgsComposerItem

//! Will be true if the legend size should be totally reset at next paint
bool mForceResize;

//! Will be true if the legend should be resized automatically to fit contents
bool mSizeToContents;
};

#endif
Expand Down
12 changes: 10 additions & 2 deletions src/ui/composer/qgscomposerlegendwidgetbase.ui
Expand Up @@ -64,8 +64,8 @@
<rect>
<x>0</x>
<y>0</y>
<width>374</width>
<height>1293</height>
<width>375</width>
<height>1506</height>
</rect>
</property>
<layout class="QVBoxLayout" name="mainLayout">
Expand Down Expand Up @@ -153,6 +153,13 @@
</item>
</widget>
</item>
<item row="4" column="0" colspan="2">
<widget class="QCheckBox" name="mCheckboxResizeContents">
<property name="text">
<string>Resize to fit contents</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
Expand Down Expand Up @@ -1034,6 +1041,7 @@
<tabstop>mTitleAlignCombo</tabstop>
<tabstop>mMapComboBox</tabstop>
<tabstop>mWrapCharLineEdit</tabstop>
<tabstop>mCheckboxResizeContents</tabstop>
<tabstop>mLegendItemColGroupBox</tabstop>
<tabstop>mCheckBoxAutoUpdate</tabstop>
<tabstop>mUpdateAllPushButton</tabstop>
Expand Down
83 changes: 83 additions & 0 deletions tests/src/python/test_qgscomposerlegend.py
Expand Up @@ -115,6 +115,89 @@ def testResizeWithMapContent(self):

QgsMapLayerRegistry.instance().removeMapLayers([point_layer])

def testResizeDisabled(self):
"""Test that test legend does not resize if auto size is disabled"""

point_path = os.path.join(TEST_DATA_DIR, 'points.shp')
point_layer = QgsVectorLayer(point_path, 'points', 'ogr')
QgsMapLayerRegistry.instance().addMapLayers([point_layer])

s = QgsMapSettings()
s.setLayers([point_layer.id()])
s.setCrsTransformEnabled(False)
composition = QgsComposition(s)
composition.setPaperSize(297, 210)

composer_map = QgsComposerMap(composition, 20, 20, 80, 80)
composer_map.setFrameEnabled(True)
composition.addComposerMap(composer_map)
composer_map.setNewExtent(point_layer.extent())

legend = QgsComposerLegend(composition)
legend.setSceneRect(QRectF(120, 20, 80, 80))
legend.setFrameEnabled(True)
legend.setFrameOutlineWidth(2)
legend.setBackgroundColor(QColor(200, 200, 200))
legend.setTitle('')
legend.setLegendFilterByMapEnabled(True)

#disable auto resizing
legend.setResizeToContents(False)

composition.addComposerLegend(legend)
legend.setComposerMap(composer_map)

composer_map.setNewExtent(QgsRectangle(-102.51, 41.16, -102.36, 41.30))

checker = QgsCompositionChecker(
'composer_legend_noresize', composition)
checker.setControlPathPrefix("composer_legend")
result, message = checker.testComposition()
self.assertTrue(result, message)

QgsMapLayerRegistry.instance().removeMapLayers([point_layer])

def testResizeDisabledCrop(self):
"""Test that if legend resizing is disabled, and legend is too small, then content is cropped"""

point_path = os.path.join(TEST_DATA_DIR, 'points.shp')
point_layer = QgsVectorLayer(point_path, 'points', 'ogr')
QgsMapLayerRegistry.instance().addMapLayers([point_layer])

s = QgsMapSettings()
s.setLayers([point_layer.id()])
s.setCrsTransformEnabled(False)
composition = QgsComposition(s)
composition.setPaperSize(297, 210)

composer_map = QgsComposerMap(composition, 20, 20, 80, 80)
composer_map.setFrameEnabled(True)
composition.addComposerMap(composer_map)
composer_map.setNewExtent(point_layer.extent())

legend = QgsComposerLegend(composition)
legend.setSceneRect(QRectF(120, 20, 20, 20))
legend.setFrameEnabled(True)
legend.setFrameOutlineWidth(2)
legend.setBackgroundColor(QColor(200, 200, 200))
legend.setTitle('')
legend.setLegendFilterByMapEnabled(True)

# disable auto resizing
legend.setResizeToContents(False)

composition.addComposerLegend(legend)
legend.setComposerMap(composer_map)

composer_map.setNewExtent(QgsRectangle(-102.51, 41.16, -102.36, 41.30))

checker = QgsCompositionChecker(
'composer_legend_noresize_crop', composition)
checker.setControlPathPrefix("composer_legend")
result, message = checker.testComposition()
self.assertTrue(result, message)

QgsMapLayerRegistry.instance().removeMapLayers([point_layer])

if __name__ == '__main__':
unittest.main()
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.

0 comments on commit a52f31a

Please sign in to comment.