[FEATURE] [needs-docs] add new vertical and horizontal equispaced distributions

This commit is contained in:
Matteo Nastasi 2019-03-09 18:18:07 +01:00
parent 4f1dd95606
commit 7c6b66be8a
9 changed files with 243 additions and 6 deletions

View File

@ -167,10 +167,12 @@
<file>themes/default/mActionDataSourceManager.svg</file>
<file>themes/default/mActionDistributeBottom.svg</file>
<file>themes/default/mActionDistributeHCenter.svg</file>
<file>themes/default/mActionDistributeHSpace.svg</file>
<file>themes/default/mActionDistributeLeft.svg</file>
<file>themes/default/mActionDistributeRight.svg</file>
<file>themes/default/mActionDistributeTop.svg</file>
<file>themes/default/mActionDistributeVCenter.svg</file>
<file>themes/default/mActionDistributeVSpace.svg</file>
<file>themes/default/mActionAddLayer.svg</file>
<file>themes/default/mActionAddMeshLayer.svg</file>
<file>themes/default/mActionAddAllToOverview.svg</file>

View File

@ -0,0 +1,49 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
version="1.1"
id="svg2"
width="24"
height="24">
<metadata
id="metadata16">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<defs
id="defs14" />
<path
id="path4"
stroke-linecap="round"
stroke="#8dbf8d"
fill="#c8fac8"
d="M5.5 7.5v10h-4v-10z" />
<path
id="path6"
stroke-opacity=".984"
stroke-linecap="round"
stroke="#81a1b4"
fill="#c8e6fa"
d="M15.5 7.5v14h-6v-14z" />
<path
id="path8"
stroke-linecap="round"
stroke="#8dbf8d"
fill="#c8fac8"
d="M23.5 7.5v8h-4v-8z" />
<path
d="m 19.498347,9.9989656 0.0028,-7.4936232 -4.000671,-0.00534 L 15.504356,10 M 9.498347,9.9989651 9.501127,2.5053419 5.500456,2.5 5.504356,10"
style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:none;fill-opacity:1;fill-rule:nonzero;stroke:#424242;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
id="path10-3-7" />
</svg>

After

Width:  |  Height:  |  Size: 1.8 KiB

View File

@ -0,0 +1,49 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
version="1.1"
id="svg3581"
width="24"
height="24">
<metadata
id="metadata3597">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<defs
id="defs3595" />
<path
id="path3583"
stroke-linecap="round"
stroke="#8dbf8d"
fill="#c8fac8"
d="M7.5 18.5h10v4h-10z" />
<path
id="path3585"
stroke-opacity=".984"
stroke-linecap="round"
stroke="#81a1b4"
fill="#c8e6fa"
d="M7.5 8.5h14v6h-14z" />
<path
id="path3589"
stroke-linecap="round"
stroke="#8dbf8d"
fill="#c8fac8"
d="M7.5.5h8v4h-8z" />
<path
d="m 9.9986182,4.5013959 -7.493624,-0.0028 -0.0053,4.0006709 7.499998,-0.0039 m -10e-4,6.0060081 -7.493623,-0.0028 -0.0053,4.000671 7.5,-0.0039"
style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:none;fill-opacity:1;fill-rule:nonzero;stroke:#424242;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
id="path10-3-7" />
</svg>

After

Width:  |  Height:  |  Size: 1.8 KiB

View File

@ -38,9 +38,11 @@ sets of layout items, e.g. aligning a group of items to top or left sides.
{
DistributeLeft,
DistributeHCenter,
DistributeHSpace,
DistributeRight,
DistributeTop,
DistributeVCenter,
DistributeVSpace,
DistributeBottom,
};

View File

@ -411,9 +411,11 @@ QgsLayoutDesignerDialog::QgsLayoutDesignerDialog( QWidget *parent, Qt::WindowFla
distributeToolButton->setToolButtonStyle( Qt::ToolButtonIconOnly );
distributeToolButton->addAction( mActionDistributeLeft );
distributeToolButton->addAction( mActionDistributeHCenter );
distributeToolButton->addAction( mActionDistributeHSpace );
distributeToolButton->addAction( mActionDistributeRight );
distributeToolButton->addAction( mActionDistributeTop );
distributeToolButton->addAction( mActionDistributeVCenter );
distributeToolButton->addAction( mActionDistributeVSpace );
distributeToolButton->addAction( mActionDistributeBottom );
distributeToolButton->setDefaultAction( mActionDistributeLeft );
mActionsToolbar->addWidget( distributeToolButton );
@ -582,6 +584,10 @@ QgsLayoutDesignerDialog::QgsLayoutDesignerDialog( QWidget *parent, Qt::WindowFla
{
mView->distributeSelectedItems( QgsLayoutAligner::DistributeHCenter );
} );
connect( mActionDistributeHSpace, &QAction::triggered, this, [ = ]
{
mView->distributeSelectedItems( QgsLayoutAligner::DistributeHSpace );
} );
connect( mActionDistributeRight, &QAction::triggered, this, [ = ]
{
mView->distributeSelectedItems( QgsLayoutAligner::DistributeRight );
@ -594,6 +600,10 @@ QgsLayoutDesignerDialog::QgsLayoutDesignerDialog( QWidget *parent, Qt::WindowFla
{
mView->distributeSelectedItems( QgsLayoutAligner::DistributeVCenter );
} );
connect( mActionDistributeVSpace, &QAction::triggered, this, [ = ]
{
mView->distributeSelectedItems( QgsLayoutAligner::DistributeVSpace );
} );
connect( mActionDistributeBottom, &QAction::triggered, this, [ = ]
{
mView->distributeSelectedItems( QgsLayoutAligner::DistributeBottom );
@ -4330,9 +4340,11 @@ void QgsLayoutDesignerDialog::toggleActions( bool layoutAvailable )
mActionAlignBottom->setEnabled( layoutAvailable );
mActionDistributeLeft->setEnabled( layoutAvailable );
mActionDistributeHCenter->setEnabled( layoutAvailable );
mActionDistributeHSpace->setEnabled( layoutAvailable );
mActionDistributeRight->setEnabled( layoutAvailable );
mActionDistributeTop->setEnabled( layoutAvailable );
mActionDistributeVCenter->setEnabled( layoutAvailable );
mActionDistributeVSpace->setEnabled( layoutAvailable );
mActionDistributeBottom->setEnabled( layoutAvailable );
mActionResizeNarrowest->setEnabled( layoutAvailable );
mActionResizeWidest->setEnabled( layoutAvailable );

View File

@ -97,6 +97,66 @@ void QgsLayoutAligner::distributeItems( QgsLayout *layout, const QList<QgsLayout
if ( items.size() < 2 )
return;
double minCoord = std::numeric_limits<double>::max();
double maxCoord = std::numeric_limits<double>::lowest();
QMap< double, QgsLayoutItem * > itemCoords;
double step;
// equispaced distribution doesn't follow the same approach of the other distribution types
if ( distribution == DistributeHSpace || distribution == DistributeVSpace )
{
double length = 0.0;
for ( QgsLayoutItem *item : items )
{
QRectF itemBBox = item->sceneBoundingRect();
double item_min, item_max;
item_min = ( distribution == DistributeHSpace ? itemBBox.left() :
itemBBox.top() );
item_max = ( distribution == DistributeHSpace ? itemBBox.right() :
itemBBox.bottom() );
minCoord = std::min( minCoord, item_min );
maxCoord = std::max( maxCoord, item_max );
length += ( item_max - item_min );
itemCoords.insert( item_min, item );
}
step = ( maxCoord - minCoord - length ) / ( items.size() - 1 );
double currentVal = minCoord;
layout->undoStack()->beginMacro( undoText( distribution ) );
for ( auto itemIt = itemCoords.constBegin(); itemIt != itemCoords.constEnd(); ++itemIt )
{
QgsLayoutItem *item = itemIt.value();
QPointF shifted = item->pos();
layout->undoStack()->beginCommand( itemIt.value(), QString() );
if ( distribution == DistributeHSpace )
{
shifted.setX( currentVal );
}
else
{
shifted.setY( currentVal );
}
QgsLayoutPoint newPos = layout->convertFromLayoutUnits( shifted, item->positionWithUnits().units() );
item->attemptMove( newPos );
layout->undoStack()->endCommand();
currentVal += ( distribution == DistributeHSpace ? item->rect().width() :
item->rect().height() ) + step;
layout->undoStack()->endMacro();
}
return;
}
auto collectReferenceCoord = [distribution]( QgsLayoutItem * item )->double
{
QRectF itemBBox = item->sceneBoundingRect();
@ -114,15 +174,16 @@ void QgsLayoutAligner::distributeItems( QgsLayout *layout, const QList<QgsLayout
return itemBBox.center().y();
case DistributeBottom:
return itemBBox.bottom();
case DistributeHSpace:
case DistributeVSpace:
// not reachable branch, just to avoid compilation warning
return 0.0;
}
// no warnings
return itemBBox.left();
};
double minCoord = std::numeric_limits<double>::max();
double maxCoord = std::numeric_limits<double>::lowest();
QMap< double, QgsLayoutItem * > itemCoords;
for ( QgsLayoutItem *item : items )
{
double refCoord = collectReferenceCoord( item );
@ -131,7 +192,7 @@ void QgsLayoutAligner::distributeItems( QgsLayout *layout, const QList<QgsLayout
itemCoords.insert( refCoord, item );
}
double step = ( maxCoord - minCoord ) / ( items.size() - 1 );
step = ( maxCoord - minCoord ) / ( items.size() - 1 );
auto distributeItemToCoord = [layout, distribution]( QgsLayoutItem * item, double refCoord )
{
@ -156,6 +217,10 @@ void QgsLayoutAligner::distributeItems( QgsLayout *layout, const QList<QgsLayout
case DistributeBottom:
shifted.setY( refCoord - item->rect().height() );
break;
case DistributeHSpace:
case DistributeVSpace:
// not reachable branch, just to avoid compilation warning
break;
}
// need to keep item units
@ -302,13 +367,17 @@ QString QgsLayoutAligner::undoText( Distribution distribution )
case DistributeLeft:
return QObject::tr( "Distribute Items by Left" );
case DistributeHCenter:
return QObject::tr( "Distribute Items by Center" );
return QObject::tr( "Distribute Items by Horizontal Center" );
case DistributeHSpace:
return QObject::tr( "Distribute Items by Horizontal Equispaced" );
case DistributeRight:
return QObject::tr( "Distribute Items by Right" );
case DistributeTop:
return QObject::tr( "Distribute Items by Top" );
case DistributeVCenter:
return QObject::tr( "Distribute Items by Vertical Center" );
case DistributeVSpace:
return QObject::tr( "Distribute Items by Vertical Equispaced" );
case DistributeBottom:
return QObject::tr( "Distribute Items by Bottom" );
}

View File

@ -54,9 +54,11 @@ class CORE_EXPORT QgsLayoutAligner
{
DistributeLeft, //!< Distribute left edges
DistributeHCenter, //!< Distribute horizontal centers
DistributeHSpace, //!< Distribute horizontal equispaced
DistributeRight, //!< Distribute right edges
DistributeTop, //!< Distribute top edges
DistributeVCenter, //!< Distribute vertical centers
DistributeVSpace, //!< Distribute vertical equispaced
DistributeBottom, //!< Distribute bottom edges
};

View File

@ -317,10 +317,12 @@
</property>
<addaction name="mActionDistributeLeft"/>
<addaction name="mActionDistributeHCenter"/>
<addaction name="mActionDistributeHSpace"/>
<addaction name="mActionDistributeRight"/>
<addaction name="separator"/>
<addaction name="mActionDistributeTop"/>
<addaction name="mActionDistributeVCenter"/>
<addaction name="mActionDistributeVSpace"/>
<addaction name="mActionDistributeBottom"/>
</widget>
<widget class="QMenu" name="menuResize">
@ -918,12 +920,24 @@
<normaloff>:/images/themes/default/mActionDistributeHCenter.svg</normaloff>:/images/themes/default/mActionDistributeHCenter.svg</iconset>
</property>
<property name="text">
<string>Distribute &amp;Centers</string>
<string>Distribute horizontal &amp;Centers</string>
</property>
<property name="toolTip">
<string>Distributes horizontal centers of items equidistantly</string>
</property>
</action>
<action name="mActionDistributeHSpace">
<property name="icon">
<iconset resource="../../../images/images.qrc">
<normaloff>:/images/themes/default/mActionDistributeHSpace.svg</normaloff>:/images/themes/default/mActionDistributeHSpace.svg</iconset>
</property>
<property name="text">
<string>Distribute &amp;Horizontally Equispaced</string>
</property>
<property name="toolTip">
<string>Distributes items equidistantly with respect to their horizontal edges</string>
</property>
</action>
<action name="mActionDistributeRight">
<property name="icon">
<iconset resource="../../../images/images.qrc">
@ -960,6 +974,18 @@
<string>Distributes vertical centers of items equidistantly</string>
</property>
</action>
<action name="mActionDistributeVSpace">
<property name="icon">
<iconset resource="../../../images/images.qrc">
<normaloff>:/images/themes/default/mActionDistributeVSpace.svg</normaloff>:/images/themes/default/mActionDistributeVSpace.svg</iconset>
</property>
<property name="text">
<string>Distribute Vertically &amp;Equispaced</string>
</property>
<property name="toolTip">
<string>Distributes items equidistantly with respect to their vertical edges</string>
</property>
</action>
<action name="mActionDistributeBottom">
<property name="icon">
<iconset resource="../../../images/images.qrc">

View File

@ -52,6 +52,7 @@ class TestQgsLayoutAligner(unittest.TestCase):
item2.attemptMove(QgsLayoutPoint(6, 10, QgsUnitTypes.LayoutMillimeters))
item2.attemptResize(QgsLayoutSize(10, 9, QgsUnitTypes.LayoutMillimeters))
l.addItem(item2)
# NOTE: item3 has measurement units specified in Centimeters, see below!
item3 = QgsLayoutItemPicture(l)
item3.attemptMove(QgsLayoutPoint(0.8, 1.2, QgsUnitTypes.LayoutCentimeters))
item3.attemptResize(QgsLayoutSize(1.8, 1.6, QgsUnitTypes.LayoutCentimeters))
@ -118,12 +119,14 @@ class TestQgsLayoutAligner(unittest.TestCase):
item2.attemptMove(QgsLayoutPoint(7, 10, QgsUnitTypes.LayoutMillimeters))
item2.attemptResize(QgsLayoutSize(10, 9, QgsUnitTypes.LayoutMillimeters))
l.addItem(item2)
# NOTE: item3 has measurement units specified in Centimeters, see below!
item3 = QgsLayoutItemPicture(l)
item3.attemptMove(QgsLayoutPoint(0.8, 1.2, QgsUnitTypes.LayoutCentimeters))
item3.attemptResize(QgsLayoutSize(1.8, 1.6, QgsUnitTypes.LayoutCentimeters))
l.addItem(item3)
QgsLayoutAligner.distributeItems(l, [item1, item2, item3], QgsLayoutAligner.DistributeLeft)
self.assertAlmostEqual(item1.positionWithUnits().x(), 4.0, 3)
self.assertEqual(item1.positionWithUnits(), QgsLayoutPoint(4, 8, QgsUnitTypes.LayoutMillimeters))
self.assertEqual(item1.sizeWithUnits(), QgsLayoutSize(18, 12, QgsUnitTypes.LayoutMillimeters))
self.assertAlmostEqual(item2.positionWithUnits().x(), 6.0, 3)
@ -188,6 +191,28 @@ class TestQgsLayoutAligner(unittest.TestCase):
self.assertEqual(item3.positionWithUnits().units(), QgsUnitTypes.LayoutCentimeters)
self.assertEqual(item3.sizeWithUnits(), QgsLayoutSize(1.8, 1.6, QgsUnitTypes.LayoutCentimeters))
QgsLayoutAligner.distributeItems(l, [item1, item2, item3], QgsLayoutAligner.DistributeHSpace)
self.assertAlmostEqual(item1.positionWithUnits().x(), 3.0, 3)
self.assertEqual(item1.positionWithUnits().units(), QgsUnitTypes.LayoutMillimeters)
self.assertEqual(item1.sizeWithUnits(), QgsLayoutSize(18, 12, QgsUnitTypes.LayoutMillimeters))
self.assertAlmostEqual(item2.positionWithUnits().x(), 9.5, 3)
self.assertEqual(item2.positionWithUnits().units(), QgsUnitTypes.LayoutMillimeters)
self.assertEqual(item2.sizeWithUnits(), QgsLayoutSize(10, 9, QgsUnitTypes.LayoutMillimeters))
self.assertAlmostEqual(item3.positionWithUnits().x(), 0.8, 3)
self.assertEqual(item3.positionWithUnits().units(), QgsUnitTypes.LayoutCentimeters)
self.assertEqual(item3.sizeWithUnits(), QgsLayoutSize(1.8, 1.6, QgsUnitTypes.LayoutCentimeters))
QgsLayoutAligner.distributeItems(l, [item1, item2, item3], QgsLayoutAligner.DistributeVSpace)
self.assertAlmostEqual(item1.positionWithUnits().y(), 8.0, 3)
self.assertEqual(item1.positionWithUnits().units(), QgsUnitTypes.LayoutMillimeters)
self.assertEqual(item1.sizeWithUnits(), QgsLayoutSize(18, 12, QgsUnitTypes.LayoutMillimeters))
self.assertAlmostEqual(item2.positionWithUnits().y(), 19.0, 3)
self.assertEqual(item2.positionWithUnits().units(), QgsUnitTypes.LayoutMillimeters)
self.assertEqual(item2.sizeWithUnits(), QgsLayoutSize(10, 9, QgsUnitTypes.LayoutMillimeters))
self.assertAlmostEqual(item3.positionWithUnits().y(), 1.15, 3)
self.assertEqual(item3.positionWithUnits().units(), QgsUnitTypes.LayoutCentimeters)
self.assertEqual(item3.sizeWithUnits(), QgsLayoutSize(1.8, 1.6, QgsUnitTypes.LayoutCentimeters))
def testResize(self):
p = QgsProject()
l = QgsLayout(p)
@ -201,6 +226,7 @@ class TestQgsLayoutAligner(unittest.TestCase):
item2.attemptMove(QgsLayoutPoint(7, 10, QgsUnitTypes.LayoutMillimeters))
item2.attemptResize(QgsLayoutSize(10, 9, QgsUnitTypes.LayoutMillimeters))
l.addItem(item2)
# NOTE: item3 has measurement units specified in Centimeters, see below!
item3 = QgsLayoutItemPicture(l)
item3.attemptMove(QgsLayoutPoint(0.8, 1.2, QgsUnitTypes.LayoutCentimeters))
item3.attemptResize(QgsLayoutSize(1.8, 1.6, QgsUnitTypes.LayoutCentimeters))