Skip to content

Commit

Permalink
Port methods for retrieving item at a point to layouts
Browse files Browse the repository at this point in the history
  • Loading branch information
nyalldawson committed Oct 6, 2017
1 parent d7bd44d commit bfa9c1d
Show file tree
Hide file tree
Showing 4 changed files with 102 additions and 1 deletion.
14 changes: 14 additions & 0 deletions python/core/layout/qgslayout.sip
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,20 @@ class QgsLayout : QGraphicsScene, QgsExpressionContextGenerator, QgsLayoutUndoOb
:rtype: QgsLayoutItem
%End

QgsLayoutItem *layoutItemAt( QPointF position, const bool ignoreLocked = false ) const;
%Docstring
Returns the topmost layout item at a specified ``position``. Ignores paper items.
If ``ignoreLocked`` is set to true any locked items will be ignored.
:rtype: QgsLayoutItem
%End

QgsLayoutItem *layoutItemAt( QPointF position, const QgsLayoutItem *belowItem, const bool ignoreLocked = false ) const;
%Docstring
Returns the topmost composer item at a specified ``position`` which is below a specified ``item``. Ignores paper items.
If ``ignoreLocked`` is set to true any locked items will be ignored.
:rtype: QgsLayoutItem
%End

void setUnits( QgsUnitTypes::LayoutUnit units );
%Docstring
Sets the native measurement ``units`` for the layout. These also form the default unit
Expand Down
36 changes: 36 additions & 0 deletions src/core/layout/qgslayout.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,42 @@ QgsLayoutItem *QgsLayout::itemByUuid( const QString &uuid )
return nullptr;
}

QgsLayoutItem *QgsLayout::layoutItemAt( QPointF position, const bool ignoreLocked ) const
{
return layoutItemAt( position, nullptr, ignoreLocked );
}

QgsLayoutItem *QgsLayout::layoutItemAt( QPointF position, const QgsLayoutItem *belowItem, const bool ignoreLocked ) const
{
//get a list of items which intersect the specified position, in descending z order
const QList<QGraphicsItem *> itemList = items( position, Qt::IntersectsItemShape, Qt::DescendingOrder );

bool foundBelowItem = false;
for ( QGraphicsItem *graphicsItem : itemList )
{
QgsLayoutItem *layoutItem = dynamic_cast<QgsLayoutItem *>( graphicsItem );
QgsLayoutItemPage *paperItem = dynamic_cast<QgsLayoutItemPage *>( layoutItem );
if ( layoutItem && !paperItem )
{
// If we are not checking for a an item below a specified item, or if we've
// already found that item, then we've found our target
if ( ( ! belowItem || foundBelowItem ) && ( !ignoreLocked || !layoutItem->isLocked() ) )
{
return layoutItem;
}
else
{
if ( layoutItem == belowItem )
{
//Target item is next in list
foundBelowItem = true;
}
}
}
}
return nullptr;
}

double QgsLayout::convertToLayoutUnits( const QgsLayoutMeasurement &measurement ) const
{
return mContext.measurementConverter().convert( measurement, mUnits ).length();
Expand Down
12 changes: 12 additions & 0 deletions src/core/layout/qgslayout.h
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,18 @@ class CORE_EXPORT QgsLayout : public QGraphicsScene, public QgsExpressionContext
*/
QgsLayoutItem *itemByUuid( const QString &uuid );

/**
* Returns the topmost layout item at a specified \a position. Ignores paper items.
* If \a ignoreLocked is set to true any locked items will be ignored.
*/
QgsLayoutItem *layoutItemAt( QPointF position, const bool ignoreLocked = false ) const;

/**
* Returns the topmost composer item at a specified \a position which is below a specified \a item. Ignores paper items.
* If \a ignoreLocked is set to true any locked items will be ignored.
*/
QgsLayoutItem *layoutItemAt( QPointF position, const QgsLayoutItem *belowItem, const bool ignoreLocked = false ) const;

/**
* Sets the native measurement \a units for the layout. These also form the default unit
* for measurements for the layout.
Expand Down
41 changes: 40 additions & 1 deletion tests/src/python/test_qgslayout.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,9 @@
QgsLayoutMeasurement,
QgsFillSymbol,
QgsReadWriteContext,
QgsLayoutItemMap)
QgsLayoutItemMap,
QgsLayoutSize,
QgsLayoutPoint)
from qgis.PyQt.QtCore import Qt, QCoreApplication, QEvent, QPointF, QRectF
from qgis.PyQt.QtTest import QSignalSpy
from qgis.PyQt.QtXml import QDomDocument
Expand Down Expand Up @@ -140,6 +142,43 @@ def testSelections(self):
self.assertEqual(len(select_changed_spy), 5)
self.assertEqual(select_changed_spy[-1][0], None)

def testLayoutItemAt(self):
p = QgsProject()
l = QgsLayout(p)

# add some items
item1 = QgsLayoutItemMap(l)
item1.attemptMove(QgsLayoutPoint(4, 8, QgsUnitTypes.LayoutMillimeters))
item1.attemptResize(QgsLayoutSize(18, 12, QgsUnitTypes.LayoutMillimeters))
l.addItem(item1)

item2 = QgsLayoutItemMap(l)
item2.attemptMove(QgsLayoutPoint(6, 10, QgsUnitTypes.LayoutMillimeters))
item2.attemptResize(QgsLayoutSize(18, 12, QgsUnitTypes.LayoutMillimeters))
l.addItem(item2)

item3 = QgsLayoutItemMap(l)
item3.attemptMove(QgsLayoutPoint(8, 12, QgsUnitTypes.LayoutMillimeters))
item3.attemptResize(QgsLayoutSize(18, 12, QgsUnitTypes.LayoutMillimeters))
item3.setLocked(True)
l.addItem(item3)

self.assertIsNone(l.layoutItemAt(QPointF(0, 0)))
self.assertIsNone(l.layoutItemAt(QPointF(100, 100)))

self.assertEqual(l.layoutItemAt(QPointF(5, 9)), item1)
self.assertEqual(l.layoutItemAt(QPointF(25, 23)), item3)
self.assertIsNone(l.layoutItemAt(QPointF(25, 23), True))
self.assertEqual(l.layoutItemAt(QPointF(7, 11)), item2)
self.assertEqual(l.layoutItemAt(QPointF(9, 13)), item3)
self.assertEqual(l.layoutItemAt(QPointF(9, 13), True), item2)

self.assertEqual(l.layoutItemAt(QPointF(9, 13), item3), item2)
self.assertEqual(l.layoutItemAt(QPointF(9, 13), item2), item1)
self.assertIsNone(l.layoutItemAt(QPointF(9, 13), item1))
item2.setLocked(True)
self.assertEqual(l.layoutItemAt(QPointF(9, 13), item3, True), item1)


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

0 comments on commit bfa9c1d

Please sign in to comment.