Skip to content

Commit 2f8c6f5

Browse files
committedJul 13, 2016
[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
1 parent 4f31ab6 commit 2f8c6f5

File tree

11 files changed

+190
-21
lines changed

11 files changed

+190
-21
lines changed
 

‎python/core/composer/qgscomposerlegend.sip

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,20 @@ class QgsComposerLegend : QgsComposerItem
4646
/** Sets item box to the whole content*/
4747
void adjustBoxSize();
4848

49+
/** Sets whether the legend should automatically resize to fit its contents.
50+
* @param enabled set to false to disable automatic resizing. The legend frame will not
51+
* be expanded to fit legend items, and items may be cropped from display.
52+
* @see resizeToContents()
53+
* @note added in QGIS 3.0
54+
*/
55+
void setResizeToContents( bool enabled );
56+
57+
/** Returns whether the legend should automatically resize to fit its contents.
58+
* @see setResizeToContents()
59+
* @note added in QGIS 3.0
60+
*/
61+
bool resizeToContents() const;
62+
4963
/** Returns pointer to the legend model*/
5064
//! @deprecated in 2.6 - use modelV2()
5165
QgsLegendModel* model() /Deprecated/;

‎src/app/composer/qgscomposerlegendwidget.cpp

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,8 @@ void QgsComposerLegendWidget::setGuiElements()
137137

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

140+
mCheckboxResizeContents->setChecked( mLegend->resizeToContents() );
141+
140142
const QgsComposerMap* map = mLegend->composerMap();
141143
mMapComboBox->setItem( map );
142144
mFontColorButton->setColor( mLegend->fontColor() );
@@ -572,6 +574,21 @@ void QgsComposerLegendWidget::composerMapChanged( QgsComposerItem* item )
572574
}
573575
}
574576

577+
void QgsComposerLegendWidget::on_mCheckboxResizeContents_toggled( bool checked )
578+
{
579+
if ( !mLegend )
580+
{
581+
return;
582+
}
583+
584+
mLegend->beginCommand( tr( "Legend resize to contents" ) );
585+
mLegend->setResizeToContents( checked );
586+
if ( checked )
587+
mLegend->adjustBoxSize();
588+
mLegend->updateItem();
589+
mLegend->endCommand();
590+
}
591+
575592
void QgsComposerLegendWidget::on_mRasterBorderGroupBox_toggled( bool state )
576593
{
577594
if ( !mLegend )
@@ -886,6 +903,7 @@ void QgsComposerLegendWidget::blockAllSignals( bool b )
886903
mRasterBorderWidthSpinBox->blockSignals( b );
887904
mWmsLegendWidthSpinBox->blockSignals( b );
888905
mWmsLegendHeightSpinBox->blockSignals( b );
906+
mCheckboxResizeContents->blockSignals( b );
889907
mTitleSpaceBottomSpinBox->blockSignals( b );
890908
}
891909

‎src/app/composer/qgscomposerlegendwidget.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@ class QgsComposerLegendWidget: public QgsComposerItemBaseWidget, private Ui::Qgs
6868
void on_mColumnSpaceSpinBox_valueChanged( double d );
6969
void on_mCheckBoxAutoUpdate_stateChanged( int state );
7070
void composerMapChanged( QgsComposerItem* item );
71+
void on_mCheckboxResizeContents_toggled( bool checked );
7172

7273
void on_mRasterBorderGroupBox_toggled( bool state );
7374
void on_mRasterBorderWidthSpinBox_valueChanged( double d );

‎src/core/composer/qgscomposerlegend.cpp

Lines changed: 47 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ QgsComposerLegend::QgsComposerLegend( QgsComposition* composition )
4444
, mInAtlas( false )
4545
, mInitialMapScaleCalculated( false )
4646
, mForceResize( false )
47+
, mSizeToContents( true )
4748
{
4849
mLegendModel2 = new QgsLegendModelV2( QgsProject::instance()->layerTreeRoot() );
4950

@@ -69,6 +70,7 @@ QgsComposerLegend::QgsComposerLegend()
6970
, mInAtlas( false )
7071
, mInitialMapScaleCalculated( false )
7172
, mForceResize( false )
73+
, mSizeToContents( true )
7274
{
7375

7476
}
@@ -120,28 +122,31 @@ void QgsComposerLegend::paint( QPainter* painter, const QStyleOptionGraphicsItem
120122
mInitialMapScaleCalculated = true;
121123

122124
QgsLegendRenderer legendRenderer( mLegendModel2, mSettings );
123-
legendRenderer.setLegendSize( mForceResize ? QSize() : rect().size() );
125+
legendRenderer.setLegendSize( mForceResize && mSizeToContents ? QSize() : rect().size() );
124126

125127
//adjust box if width or height is too small
126-
QSizeF size = legendRenderer.minimumSize();
127-
if ( mForceResize )
128+
if ( mSizeToContents )
128129
{
129-
mForceResize = false;
130-
//set new rect, respecting position mode and data defined size/position
131-
QRectF targetRect = QRectF( pos().x(), pos().y(), size.width(), size.height() );
132-
setSceneRect( evalItemRect( targetRect, true ) );
133-
}
134-
else if ( size.height() > rect().height() || size.width() > rect().width() )
135-
{
136-
//need to resize box
137-
QRectF targetRect = QRectF( pos().x(), pos().y(), rect().width(), rect().height() );
138-
if ( size.height() > targetRect.height() )
139-
targetRect.setHeight( size.height() );
140-
if ( size.width() > rect().width() )
141-
targetRect.setWidth( size.width() );
142-
143-
//set new rect, respecting position mode and data defined size/position
144-
setSceneRect( evalItemRect( targetRect, true ) );
130+
QSizeF size = legendRenderer.minimumSize();
131+
if ( mForceResize )
132+
{
133+
mForceResize = false;
134+
//set new rect, respecting position mode and data defined size/position
135+
QRectF targetRect = QRectF( pos().x(), pos().y(), size.width(), size.height() );
136+
setSceneRect( evalItemRect( targetRect, true ) );
137+
}
138+
else if ( size.height() > rect().height() || size.width() > rect().width() )
139+
{
140+
//need to resize box
141+
QRectF targetRect = QRectF( pos().x(), pos().y(), rect().width(), rect().height() );
142+
if ( size.height() > targetRect.height() )
143+
targetRect.setHeight( size.height() );
144+
if ( size.width() > rect().width() )
145+
targetRect.setWidth( size.width() );
146+
147+
//set new rect, respecting position mode and data defined size/position
148+
setSceneRect( evalItemRect( targetRect, true ) );
149+
}
145150
}
146151

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

158+
if ( !mSizeToContents )
159+
{
160+
// set a clip region to crop out parts of legend which don't fit
161+
QRectF thisPaintRect = QRectF( 0, 0, rect().width(), rect().height() );
162+
painter->setClipRect( thisPaintRect );
163+
}
164+
153165
legendRenderer.drawLegend( painter );
154166

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

181193
void QgsComposerLegend::adjustBoxSize()
182194
{
195+
if ( !mSizeToContents )
196+
return;
197+
183198
if ( !mInitialMapScaleCalculated )
184199
{
185200
// 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()
200215
}
201216
}
202217

218+
void QgsComposerLegend::setResizeToContents( bool enabled )
219+
{
220+
mSizeToContents = enabled;
221+
}
222+
223+
bool QgsComposerLegend::resizeToContents() const
224+
{
225+
return mSizeToContents;
226+
}
203227

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

389+
composerLegendElem.setAttribute( "resizeToContents", mSizeToContents );
390+
365391
if ( mComposerMap )
366392
{
367393
composerLegendElem.setAttribute( "map", mComposerMap->id() );
@@ -488,6 +514,8 @@ bool QgsComposerLegend::readXML( const QDomElement& itemElem, const QDomDocument
488514

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

517+
mSizeToContents = itemElem.attribute( "resizeToContents", "1" ) != "0";
518+
491519
//composer map
492520
mLegendFilterByMap = itemElem.attribute( "legendFilterByMap", "0" ).toInt();
493521
if ( !itemElem.attribute( "map" ).isEmpty() )

‎src/core/composer/qgscomposerlegend.h

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,20 @@ class CORE_EXPORT QgsComposerLegend : public QgsComposerItem
7575
/** Sets item box to the whole content*/
7676
void adjustBoxSize();
7777

78+
/** Sets whether the legend should automatically resize to fit its contents.
79+
* @param enabled set to false to disable automatic resizing. The legend frame will not
80+
* be expanded to fit legend items, and items may be cropped from display.
81+
* @see resizeToContents()
82+
* @note added in QGIS 3.0
83+
*/
84+
void setResizeToContents( bool enabled );
85+
86+
/** Returns whether the legend should automatically resize to fit its contents.
87+
* @see setResizeToContents()
88+
* @note added in QGIS 3.0
89+
*/
90+
bool resizeToContents() const;
91+
7892
/** Returns pointer to the legend model*/
7993
//! @deprecated in 2.6 - use modelV2()
8094
Q_DECL_DEPRECATED QgsLegendModel* model() {return &mLegendModel;}
@@ -299,6 +313,9 @@ class CORE_EXPORT QgsComposerLegend : public QgsComposerItem
299313

300314
//! Will be true if the legend size should be totally reset at next paint
301315
bool mForceResize;
316+
317+
//! Will be true if the legend should be resized automatically to fit contents
318+
bool mSizeToContents;
302319
};
303320

304321
#endif

‎src/ui/composer/qgscomposerlegendwidgetbase.ui

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -64,8 +64,8 @@
6464
<rect>
6565
<x>0</x>
6666
<y>0</y>
67-
<width>374</width>
68-
<height>1293</height>
67+
<width>375</width>
68+
<height>1506</height>
6969
</rect>
7070
</property>
7171
<layout class="QVBoxLayout" name="mainLayout">
@@ -153,6 +153,13 @@
153153
</item>
154154
</widget>
155155
</item>
156+
<item row="4" column="0" colspan="2">
157+
<widget class="QCheckBox" name="mCheckboxResizeContents">
158+
<property name="text">
159+
<string>Resize to fit contents</string>
160+
</property>
161+
</widget>
162+
</item>
156163
</layout>
157164
</widget>
158165
</item>
@@ -1034,6 +1041,7 @@
10341041
<tabstop>mTitleAlignCombo</tabstop>
10351042
<tabstop>mMapComboBox</tabstop>
10361043
<tabstop>mWrapCharLineEdit</tabstop>
1044+
<tabstop>mCheckboxResizeContents</tabstop>
10371045
<tabstop>mLegendItemColGroupBox</tabstop>
10381046
<tabstop>mCheckBoxAutoUpdate</tabstop>
10391047
<tabstop>mUpdateAllPushButton</tabstop>

‎tests/src/python/test_qgscomposerlegend.py

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,89 @@ def testResizeWithMapContent(self):
115115

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

118+
def testResizeDisabled(self):
119+
"""Test that test legend does not resize if auto size is disabled"""
120+
121+
point_path = os.path.join(TEST_DATA_DIR, 'points.shp')
122+
point_layer = QgsVectorLayer(point_path, 'points', 'ogr')
123+
QgsMapLayerRegistry.instance().addMapLayers([point_layer])
124+
125+
s = QgsMapSettings()
126+
s.setLayers([point_layer.id()])
127+
s.setCrsTransformEnabled(False)
128+
composition = QgsComposition(s)
129+
composition.setPaperSize(297, 210)
130+
131+
composer_map = QgsComposerMap(composition, 20, 20, 80, 80)
132+
composer_map.setFrameEnabled(True)
133+
composition.addComposerMap(composer_map)
134+
composer_map.setNewExtent(point_layer.extent())
135+
136+
legend = QgsComposerLegend(composition)
137+
legend.setSceneRect(QRectF(120, 20, 80, 80))
138+
legend.setFrameEnabled(True)
139+
legend.setFrameOutlineWidth(2)
140+
legend.setBackgroundColor(QColor(200, 200, 200))
141+
legend.setTitle('')
142+
legend.setLegendFilterByMapEnabled(True)
143+
144+
#disable auto resizing
145+
legend.setResizeToContents(False)
146+
147+
composition.addComposerLegend(legend)
148+
legend.setComposerMap(composer_map)
149+
150+
composer_map.setNewExtent(QgsRectangle(-102.51, 41.16, -102.36, 41.30))
151+
152+
checker = QgsCompositionChecker(
153+
'composer_legend_noresize', composition)
154+
checker.setControlPathPrefix("composer_legend")
155+
result, message = checker.testComposition()
156+
self.assertTrue(result, message)
157+
158+
QgsMapLayerRegistry.instance().removeMapLayers([point_layer])
159+
160+
def testResizeDisabledCrop(self):
161+
"""Test that if legend resizing is disabled, and legend is too small, then content is cropped"""
162+
163+
point_path = os.path.join(TEST_DATA_DIR, 'points.shp')
164+
point_layer = QgsVectorLayer(point_path, 'points', 'ogr')
165+
QgsMapLayerRegistry.instance().addMapLayers([point_layer])
166+
167+
s = QgsMapSettings()
168+
s.setLayers([point_layer.id()])
169+
s.setCrsTransformEnabled(False)
170+
composition = QgsComposition(s)
171+
composition.setPaperSize(297, 210)
172+
173+
composer_map = QgsComposerMap(composition, 20, 20, 80, 80)
174+
composer_map.setFrameEnabled(True)
175+
composition.addComposerMap(composer_map)
176+
composer_map.setNewExtent(point_layer.extent())
177+
178+
legend = QgsComposerLegend(composition)
179+
legend.setSceneRect(QRectF(120, 20, 20, 20))
180+
legend.setFrameEnabled(True)
181+
legend.setFrameOutlineWidth(2)
182+
legend.setBackgroundColor(QColor(200, 200, 200))
183+
legend.setTitle('')
184+
legend.setLegendFilterByMapEnabled(True)
185+
186+
# disable auto resizing
187+
legend.setResizeToContents(False)
188+
189+
composition.addComposerLegend(legend)
190+
legend.setComposerMap(composer_map)
191+
192+
composer_map.setNewExtent(QgsRectangle(-102.51, 41.16, -102.36, 41.30))
193+
194+
checker = QgsCompositionChecker(
195+
'composer_legend_noresize_crop', composition)
196+
checker.setControlPathPrefix("composer_legend")
197+
result, message = checker.testComposition()
198+
self.assertTrue(result, message)
199+
200+
QgsMapLayerRegistry.instance().removeMapLayers([point_layer])
118201

119202
if __name__ == '__main__':
120203
unittest.main()

1 commit comments

Comments
 (1)

DelazJ commented on Jul 13, 2016

@DelazJ
Contributor

@nyalldawson doesn't this need to be documented (feature tag)?

Please sign in to comment.