Skip to content
Permalink
Browse files

[composer] Add a checkbox for legends to prevent automatic resizing

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 62cdb0c commit b9954bcc1f5b32aa0a50f02ae101b869b65797f0
@@ -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/;
@@ -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() );
@@ -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 )
@@ -886,6 +903,7 @@ void QgsComposerLegendWidget::blockAllSignals( bool b )
mRasterBorderWidthSpinBox->blockSignals( b );
mWmsLegendWidthSpinBox->blockSignals( b );
mWmsLegendHeightSpinBox->blockSignals( b );
mCheckboxResizeContents->blockSignals( b );
mTitleSpaceBottomSpinBox->blockSignals( b );
}

@@ -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 );
@@ -44,6 +44,7 @@ QgsComposerLegend::QgsComposerLegend( QgsComposition* composition )
, mInAtlas( false )
, mInitialMapScaleCalculated( false )
, mForceResize( false )
, mSizeToContents( true )
{
mLegendModel2 = new QgsLegendModelV2( QgsProject::instance()->layerTreeRoot() );

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

}
@@ -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 );
@@ -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();
@@ -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
@@ -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 )
{
@@ -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() );
@@ -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() )
@@ -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;}
@@ -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
@@ -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">
@@ -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>
@@ -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>
@@ -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()
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.

0 comments on commit b9954bc

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