Skip to content

Commit 7c6b66b

Browse files
committedMar 9, 2019
[FEATURE] [needs-docs] add new vertical and horizontal equispaced distributions
1 parent 4f1dd95 commit 7c6b66b

File tree

9 files changed

+243
-6
lines changed

9 files changed

+243
-6
lines changed
 

‎images/images.qrc

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -167,10 +167,12 @@
167167
<file>themes/default/mActionDataSourceManager.svg</file>
168168
<file>themes/default/mActionDistributeBottom.svg</file>
169169
<file>themes/default/mActionDistributeHCenter.svg</file>
170+
<file>themes/default/mActionDistributeHSpace.svg</file>
170171
<file>themes/default/mActionDistributeLeft.svg</file>
171172
<file>themes/default/mActionDistributeRight.svg</file>
172173
<file>themes/default/mActionDistributeTop.svg</file>
173174
<file>themes/default/mActionDistributeVCenter.svg</file>
175+
<file>themes/default/mActionDistributeVSpace.svg</file>
174176
<file>themes/default/mActionAddLayer.svg</file>
175177
<file>themes/default/mActionAddMeshLayer.svg</file>
176178
<file>themes/default/mActionAddAllToOverview.svg</file>
Lines changed: 49 additions & 0 deletions
Loading
Lines changed: 49 additions & 0 deletions
Loading

‎python/core/auto_generated/layout/qgslayoutaligner.sip.in

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,9 +38,11 @@ sets of layout items, e.g. aligning a group of items to top or left sides.
3838
{
3939
DistributeLeft,
4040
DistributeHCenter,
41+
DistributeHSpace,
4142
DistributeRight,
4243
DistributeTop,
4344
DistributeVCenter,
45+
DistributeVSpace,
4446
DistributeBottom,
4547
};
4648

‎src/app/layout/qgslayoutdesignerdialog.cpp

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -411,9 +411,11 @@ QgsLayoutDesignerDialog::QgsLayoutDesignerDialog( QWidget *parent, Qt::WindowFla
411411
distributeToolButton->setToolButtonStyle( Qt::ToolButtonIconOnly );
412412
distributeToolButton->addAction( mActionDistributeLeft );
413413
distributeToolButton->addAction( mActionDistributeHCenter );
414+
distributeToolButton->addAction( mActionDistributeHSpace );
414415
distributeToolButton->addAction( mActionDistributeRight );
415416
distributeToolButton->addAction( mActionDistributeTop );
416417
distributeToolButton->addAction( mActionDistributeVCenter );
418+
distributeToolButton->addAction( mActionDistributeVSpace );
417419
distributeToolButton->addAction( mActionDistributeBottom );
418420
distributeToolButton->setDefaultAction( mActionDistributeLeft );
419421
mActionsToolbar->addWidget( distributeToolButton );
@@ -582,6 +584,10 @@ QgsLayoutDesignerDialog::QgsLayoutDesignerDialog( QWidget *parent, Qt::WindowFla
582584
{
583585
mView->distributeSelectedItems( QgsLayoutAligner::DistributeHCenter );
584586
} );
587+
connect( mActionDistributeHSpace, &QAction::triggered, this, [ = ]
588+
{
589+
mView->distributeSelectedItems( QgsLayoutAligner::DistributeHSpace );
590+
} );
585591
connect( mActionDistributeRight, &QAction::triggered, this, [ = ]
586592
{
587593
mView->distributeSelectedItems( QgsLayoutAligner::DistributeRight );
@@ -594,6 +600,10 @@ QgsLayoutDesignerDialog::QgsLayoutDesignerDialog( QWidget *parent, Qt::WindowFla
594600
{
595601
mView->distributeSelectedItems( QgsLayoutAligner::DistributeVCenter );
596602
} );
603+
connect( mActionDistributeVSpace, &QAction::triggered, this, [ = ]
604+
{
605+
mView->distributeSelectedItems( QgsLayoutAligner::DistributeVSpace );
606+
} );
597607
connect( mActionDistributeBottom, &QAction::triggered, this, [ = ]
598608
{
599609
mView->distributeSelectedItems( QgsLayoutAligner::DistributeBottom );
@@ -4330,9 +4340,11 @@ void QgsLayoutDesignerDialog::toggleActions( bool layoutAvailable )
43304340
mActionAlignBottom->setEnabled( layoutAvailable );
43314341
mActionDistributeLeft->setEnabled( layoutAvailable );
43324342
mActionDistributeHCenter->setEnabled( layoutAvailable );
4343+
mActionDistributeHSpace->setEnabled( layoutAvailable );
43334344
mActionDistributeRight->setEnabled( layoutAvailable );
43344345
mActionDistributeTop->setEnabled( layoutAvailable );
43354346
mActionDistributeVCenter->setEnabled( layoutAvailable );
4347+
mActionDistributeVSpace->setEnabled( layoutAvailable );
43364348
mActionDistributeBottom->setEnabled( layoutAvailable );
43374349
mActionResizeNarrowest->setEnabled( layoutAvailable );
43384350
mActionResizeWidest->setEnabled( layoutAvailable );

‎src/core/layout/qgslayoutaligner.cpp

Lines changed: 74 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,66 @@ void QgsLayoutAligner::distributeItems( QgsLayout *layout, const QList<QgsLayout
9797
if ( items.size() < 2 )
9898
return;
9999

100+
double minCoord = std::numeric_limits<double>::max();
101+
double maxCoord = std::numeric_limits<double>::lowest();
102+
QMap< double, QgsLayoutItem * > itemCoords;
103+
104+
double step;
105+
106+
// equispaced distribution doesn't follow the same approach of the other distribution types
107+
if ( distribution == DistributeHSpace || distribution == DistributeVSpace )
108+
{
109+
double length = 0.0;
110+
111+
for ( QgsLayoutItem *item : items )
112+
{
113+
QRectF itemBBox = item->sceneBoundingRect();
114+
double item_min, item_max;
115+
116+
item_min = ( distribution == DistributeHSpace ? itemBBox.left() :
117+
itemBBox.top() );
118+
item_max = ( distribution == DistributeHSpace ? itemBBox.right() :
119+
itemBBox.bottom() );
120+
121+
minCoord = std::min( minCoord, item_min );
122+
maxCoord = std::max( maxCoord, item_max );
123+
length += ( item_max - item_min );
124+
itemCoords.insert( item_min, item );
125+
}
126+
step = ( maxCoord - minCoord - length ) / ( items.size() - 1 );
127+
128+
double currentVal = minCoord;
129+
layout->undoStack()->beginMacro( undoText( distribution ) );
130+
for ( auto itemIt = itemCoords.constBegin(); itemIt != itemCoords.constEnd(); ++itemIt )
131+
{
132+
QgsLayoutItem *item = itemIt.value();
133+
QPointF shifted = item->pos();
134+
135+
layout->undoStack()->beginCommand( itemIt.value(), QString() );
136+
137+
if ( distribution == DistributeHSpace )
138+
{
139+
shifted.setX( currentVal );
140+
}
141+
else
142+
{
143+
shifted.setY( currentVal );
144+
}
145+
146+
QgsLayoutPoint newPos = layout->convertFromLayoutUnits( shifted, item->positionWithUnits().units() );
147+
item->attemptMove( newPos );
148+
149+
layout->undoStack()->endCommand();
150+
151+
currentVal += ( distribution == DistributeHSpace ? item->rect().width() :
152+
item->rect().height() ) + step;
153+
154+
layout->undoStack()->endMacro();
155+
}
156+
157+
return;
158+
}
159+
100160
auto collectReferenceCoord = [distribution]( QgsLayoutItem * item )->double
101161
{
102162
QRectF itemBBox = item->sceneBoundingRect();
@@ -114,15 +174,16 @@ void QgsLayoutAligner::distributeItems( QgsLayout *layout, const QList<QgsLayout
114174
return itemBBox.center().y();
115175
case DistributeBottom:
116176
return itemBBox.bottom();
177+
case DistributeHSpace:
178+
case DistributeVSpace:
179+
// not reachable branch, just to avoid compilation warning
180+
return 0.0;
117181
}
118182
// no warnings
119183
return itemBBox.left();
120184
};
121185

122186

123-
double minCoord = std::numeric_limits<double>::max();
124-
double maxCoord = std::numeric_limits<double>::lowest();
125-
QMap< double, QgsLayoutItem * > itemCoords;
126187
for ( QgsLayoutItem *item : items )
127188
{
128189
double refCoord = collectReferenceCoord( item );
@@ -131,7 +192,7 @@ void QgsLayoutAligner::distributeItems( QgsLayout *layout, const QList<QgsLayout
131192
itemCoords.insert( refCoord, item );
132193
}
133194

134-
double step = ( maxCoord - minCoord ) / ( items.size() - 1 );
195+
step = ( maxCoord - minCoord ) / ( items.size() - 1 );
135196

136197
auto distributeItemToCoord = [layout, distribution]( QgsLayoutItem * item, double refCoord )
137198
{
@@ -156,6 +217,10 @@ void QgsLayoutAligner::distributeItems( QgsLayout *layout, const QList<QgsLayout
156217
case DistributeBottom:
157218
shifted.setY( refCoord - item->rect().height() );
158219
break;
220+
case DistributeHSpace:
221+
case DistributeVSpace:
222+
// not reachable branch, just to avoid compilation warning
223+
break;
159224
}
160225

161226
// need to keep item units
@@ -302,13 +367,17 @@ QString QgsLayoutAligner::undoText( Distribution distribution )
302367
case DistributeLeft:
303368
return QObject::tr( "Distribute Items by Left" );
304369
case DistributeHCenter:
305-
return QObject::tr( "Distribute Items by Center" );
370+
return QObject::tr( "Distribute Items by Horizontal Center" );
371+
case DistributeHSpace:
372+
return QObject::tr( "Distribute Items by Horizontal Equispaced" );
306373
case DistributeRight:
307374
return QObject::tr( "Distribute Items by Right" );
308375
case DistributeTop:
309376
return QObject::tr( "Distribute Items by Top" );
310377
case DistributeVCenter:
311378
return QObject::tr( "Distribute Items by Vertical Center" );
379+
case DistributeVSpace:
380+
return QObject::tr( "Distribute Items by Vertical Equispaced" );
312381
case DistributeBottom:
313382
return QObject::tr( "Distribute Items by Bottom" );
314383
}

‎src/core/layout/qgslayoutaligner.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,9 +54,11 @@ class CORE_EXPORT QgsLayoutAligner
5454
{
5555
DistributeLeft, //!< Distribute left edges
5656
DistributeHCenter, //!< Distribute horizontal centers
57+
DistributeHSpace, //!< Distribute horizontal equispaced
5758
DistributeRight, //!< Distribute right edges
5859
DistributeTop, //!< Distribute top edges
5960
DistributeVCenter, //!< Distribute vertical centers
61+
DistributeVSpace, //!< Distribute vertical equispaced
6062
DistributeBottom, //!< Distribute bottom edges
6163
};
6264

‎src/ui/layout/qgslayoutdesignerbase.ui

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -317,10 +317,12 @@
317317
</property>
318318
<addaction name="mActionDistributeLeft"/>
319319
<addaction name="mActionDistributeHCenter"/>
320+
<addaction name="mActionDistributeHSpace"/>
320321
<addaction name="mActionDistributeRight"/>
321322
<addaction name="separator"/>
322323
<addaction name="mActionDistributeTop"/>
323324
<addaction name="mActionDistributeVCenter"/>
325+
<addaction name="mActionDistributeVSpace"/>
324326
<addaction name="mActionDistributeBottom"/>
325327
</widget>
326328
<widget class="QMenu" name="menuResize">
@@ -918,12 +920,24 @@
918920
<normaloff>:/images/themes/default/mActionDistributeHCenter.svg</normaloff>:/images/themes/default/mActionDistributeHCenter.svg</iconset>
919921
</property>
920922
<property name="text">
921-
<string>Distribute &amp;Centers</string>
923+
<string>Distribute horizontal &amp;Centers</string>
922924
</property>
923925
<property name="toolTip">
924926
<string>Distributes horizontal centers of items equidistantly</string>
925927
</property>
926928
</action>
929+
<action name="mActionDistributeHSpace">
930+
<property name="icon">
931+
<iconset resource="../../../images/images.qrc">
932+
<normaloff>:/images/themes/default/mActionDistributeHSpace.svg</normaloff>:/images/themes/default/mActionDistributeHSpace.svg</iconset>
933+
</property>
934+
<property name="text">
935+
<string>Distribute &amp;Horizontally Equispaced</string>
936+
</property>
937+
<property name="toolTip">
938+
<string>Distributes items equidistantly with respect to their horizontal edges</string>
939+
</property>
940+
</action>
927941
<action name="mActionDistributeRight">
928942
<property name="icon">
929943
<iconset resource="../../../images/images.qrc">
@@ -960,6 +974,18 @@
960974
<string>Distributes vertical centers of items equidistantly</string>
961975
</property>
962976
</action>
977+
<action name="mActionDistributeVSpace">
978+
<property name="icon">
979+
<iconset resource="../../../images/images.qrc">
980+
<normaloff>:/images/themes/default/mActionDistributeVSpace.svg</normaloff>:/images/themes/default/mActionDistributeVSpace.svg</iconset>
981+
</property>
982+
<property name="text">
983+
<string>Distribute Vertically &amp;Equispaced</string>
984+
</property>
985+
<property name="toolTip">
986+
<string>Distributes items equidistantly with respect to their vertical edges</string>
987+
</property>
988+
</action>
963989
<action name="mActionDistributeBottom">
964990
<property name="icon">
965991
<iconset resource="../../../images/images.qrc">

‎tests/src/python/test_qgslayoutaligner.py

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ def testAlign(self):
5252
item2.attemptMove(QgsLayoutPoint(6, 10, QgsUnitTypes.LayoutMillimeters))
5353
item2.attemptResize(QgsLayoutSize(10, 9, QgsUnitTypes.LayoutMillimeters))
5454
l.addItem(item2)
55+
# NOTE: item3 has measurement units specified in Centimeters, see below!
5556
item3 = QgsLayoutItemPicture(l)
5657
item3.attemptMove(QgsLayoutPoint(0.8, 1.2, QgsUnitTypes.LayoutCentimeters))
5758
item3.attemptResize(QgsLayoutSize(1.8, 1.6, QgsUnitTypes.LayoutCentimeters))
@@ -118,12 +119,14 @@ def testDistribute(self):
118119
item2.attemptMove(QgsLayoutPoint(7, 10, QgsUnitTypes.LayoutMillimeters))
119120
item2.attemptResize(QgsLayoutSize(10, 9, QgsUnitTypes.LayoutMillimeters))
120121
l.addItem(item2)
122+
# NOTE: item3 has measurement units specified in Centimeters, see below!
121123
item3 = QgsLayoutItemPicture(l)
122124
item3.attemptMove(QgsLayoutPoint(0.8, 1.2, QgsUnitTypes.LayoutCentimeters))
123125
item3.attemptResize(QgsLayoutSize(1.8, 1.6, QgsUnitTypes.LayoutCentimeters))
124126
l.addItem(item3)
125127

126128
QgsLayoutAligner.distributeItems(l, [item1, item2, item3], QgsLayoutAligner.DistributeLeft)
129+
self.assertAlmostEqual(item1.positionWithUnits().x(), 4.0, 3)
127130
self.assertEqual(item1.positionWithUnits(), QgsLayoutPoint(4, 8, QgsUnitTypes.LayoutMillimeters))
128131
self.assertEqual(item1.sizeWithUnits(), QgsLayoutSize(18, 12, QgsUnitTypes.LayoutMillimeters))
129132
self.assertAlmostEqual(item2.positionWithUnits().x(), 6.0, 3)
@@ -188,6 +191,28 @@ def testDistribute(self):
188191
self.assertEqual(item3.positionWithUnits().units(), QgsUnitTypes.LayoutCentimeters)
189192
self.assertEqual(item3.sizeWithUnits(), QgsLayoutSize(1.8, 1.6, QgsUnitTypes.LayoutCentimeters))
190193

194+
QgsLayoutAligner.distributeItems(l, [item1, item2, item3], QgsLayoutAligner.DistributeHSpace)
195+
self.assertAlmostEqual(item1.positionWithUnits().x(), 3.0, 3)
196+
self.assertEqual(item1.positionWithUnits().units(), QgsUnitTypes.LayoutMillimeters)
197+
self.assertEqual(item1.sizeWithUnits(), QgsLayoutSize(18, 12, QgsUnitTypes.LayoutMillimeters))
198+
self.assertAlmostEqual(item2.positionWithUnits().x(), 9.5, 3)
199+
self.assertEqual(item2.positionWithUnits().units(), QgsUnitTypes.LayoutMillimeters)
200+
self.assertEqual(item2.sizeWithUnits(), QgsLayoutSize(10, 9, QgsUnitTypes.LayoutMillimeters))
201+
self.assertAlmostEqual(item3.positionWithUnits().x(), 0.8, 3)
202+
self.assertEqual(item3.positionWithUnits().units(), QgsUnitTypes.LayoutCentimeters)
203+
self.assertEqual(item3.sizeWithUnits(), QgsLayoutSize(1.8, 1.6, QgsUnitTypes.LayoutCentimeters))
204+
205+
QgsLayoutAligner.distributeItems(l, [item1, item2, item3], QgsLayoutAligner.DistributeVSpace)
206+
self.assertAlmostEqual(item1.positionWithUnits().y(), 8.0, 3)
207+
self.assertEqual(item1.positionWithUnits().units(), QgsUnitTypes.LayoutMillimeters)
208+
self.assertEqual(item1.sizeWithUnits(), QgsLayoutSize(18, 12, QgsUnitTypes.LayoutMillimeters))
209+
self.assertAlmostEqual(item2.positionWithUnits().y(), 19.0, 3)
210+
self.assertEqual(item2.positionWithUnits().units(), QgsUnitTypes.LayoutMillimeters)
211+
self.assertEqual(item2.sizeWithUnits(), QgsLayoutSize(10, 9, QgsUnitTypes.LayoutMillimeters))
212+
self.assertAlmostEqual(item3.positionWithUnits().y(), 1.15, 3)
213+
self.assertEqual(item3.positionWithUnits().units(), QgsUnitTypes.LayoutCentimeters)
214+
self.assertEqual(item3.sizeWithUnits(), QgsLayoutSize(1.8, 1.6, QgsUnitTypes.LayoutCentimeters))
215+
191216
def testResize(self):
192217
p = QgsProject()
193218
l = QgsLayout(p)
@@ -201,6 +226,7 @@ def testResize(self):
201226
item2.attemptMove(QgsLayoutPoint(7, 10, QgsUnitTypes.LayoutMillimeters))
202227
item2.attemptResize(QgsLayoutSize(10, 9, QgsUnitTypes.LayoutMillimeters))
203228
l.addItem(item2)
229+
# NOTE: item3 has measurement units specified in Centimeters, see below!
204230
item3 = QgsLayoutItemPicture(l)
205231
item3.attemptMove(QgsLayoutPoint(0.8, 1.2, QgsUnitTypes.LayoutCentimeters))
206232
item3.attemptResize(QgsLayoutSize(1.8, 1.6, QgsUnitTypes.LayoutCentimeters))

0 commit comments

Comments
 (0)
Please sign in to comment.