Skip to content

Commit

Permalink
Port item selection and lock related code from composer
Browse files Browse the repository at this point in the history
  • Loading branch information
nyalldawson committed Oct 6, 2017
1 parent 4e61ea8 commit 0b18829
Show file tree
Hide file tree
Showing 8 changed files with 157 additions and 2 deletions.
9 changes: 9 additions & 0 deletions python/core/layout/qgslayout.sip
Expand Up @@ -66,6 +66,15 @@ class QgsLayout : QGraphicsScene, QgsExpressionContextGenerator, QgsLayoutUndoOb
%End


QList<QgsLayoutItem *> selectedLayoutItems( const bool includeLockedItems = true );
%Docstring
Returns list of selected layout items.

If ``includeLockedItems`` is set to true, then locked items will also be included
in the returned list.
:rtype: list of QgsLayoutItem
%End

QgsLayoutItem *itemByUuid( const QString &uuid );
%Docstring
Returns the layout item with matching ``uuid`` unique identifier, or a None
Expand Down
22 changes: 22 additions & 0 deletions python/core/layout/qgslayoutitem.sip
Expand Up @@ -102,6 +102,21 @@ class QgsLayoutItem : QgsLayoutObject, QGraphicsRectItem, QgsLayoutUndoObjectInt
.. seealso:: uuid()
%End

void setLocked( const bool locked );
%Docstring
Sets whether the item is ``locked``, preventing mouse interactions with the item.
.. seealso:: isLocked()
.. seealso:: lockChanged()
%End

bool isLocked() const;
%Docstring
Returns true if the item is locked, and cannot be interacted with using the mouse.
.. seealso:: setLocked()
.. seealso:: lockChanged()
:rtype: bool
%End

virtual void paint( QPainter *painter, const QStyleOptionGraphicsItem *itemStyle, QWidget *pWidget );

%Docstring
Expand Down Expand Up @@ -372,6 +387,13 @@ class QgsLayoutItem : QgsLayoutObject, QGraphicsRectItem, QgsLayoutUndoObjectInt
Emitted if the item's frame style changes.
%End

void lockChanged();
%Docstring
Emitted if the item's lock status changes.
.. seealso:: isLocked()
.. seealso:: setLocked()
%End

protected:

virtual void drawDebugRect( QPainter *painter );
Expand Down
18 changes: 18 additions & 0 deletions src/core/layout/qgslayout.cpp
Expand Up @@ -15,6 +15,7 @@
***************************************************************************/

#include "qgslayout.h"
#include "qgslayoutitem.h"
#include "qgslayoutpagecollection.h"
#include "qgslayoutguidecollection.h"
#include "qgsreadwritecontext.h"
Expand Down Expand Up @@ -45,6 +46,23 @@ QgsProject *QgsLayout::project() const
return mProject;
}

QList<QgsLayoutItem *> QgsLayout::selectedLayoutItems( const bool includeLockedItems )
{
QList<QgsLayoutItem *> layoutItemList;

const QList<QGraphicsItem *> graphicsItemList = selectedItems();
for ( QGraphicsItem *item : graphicsItemList )
{
QgsLayoutItem *layoutItem = dynamic_cast<QgsLayoutItem *>( item );
if ( layoutItem && ( includeLockedItems || !layoutItem->isLocked() ) )
{
layoutItemList.push_back( layoutItem );
}
}

return layoutItemList;
}

QgsLayoutItem *QgsLayout::itemByUuid( const QString &uuid )
{
QList<QgsLayoutItem *> itemList;
Expand Down
8 changes: 8 additions & 0 deletions src/core/layout/qgslayout.h
Expand Up @@ -104,6 +104,14 @@ class CORE_EXPORT QgsLayout : public QGraphicsScene, public QgsExpressionContext
}
}

/**
* Returns list of selected layout items.
*
* If \a includeLockedItems is set to true, then locked items will also be included
* in the returned list.
*/
QList<QgsLayoutItem *> selectedLayoutItems( const bool includeLockedItems = true );

/**
* Returns the layout item with matching \a uuid unique identifier, or a nullptr
* if a matching item could not be found.
Expand Down
23 changes: 22 additions & 1 deletion src/core/layout/qgslayoutitem.cpp
Expand Up @@ -33,7 +33,8 @@ QgsLayoutItem::QgsLayoutItem( QgsLayout *layout )
setZValue( QgsLayout::ZItem );

// needed to access current view transform during paint operations
setFlags( flags() | QGraphicsItem::ItemUsesExtendedStyleOption );
setFlags( flags() | QGraphicsItem::ItemUsesExtendedStyleOption | QGraphicsItem::ItemIsSelectable );

setCacheMode( QGraphicsItem::DeviceCoordinateCache );

//record initial position
Expand Down Expand Up @@ -70,6 +71,26 @@ void QgsLayoutItem::setId( const QString &id )
#endif
}

void QgsLayoutItem::setLocked( const bool locked )
{
if ( locked == mIsLocked )
{
return;
}

mIsLocked = locked;

#if 0 //TODO
//inform model that id data has changed
if ( mLayout )
{
mLayout->itemsModel()->updateItemLockStatus( this );
}
#endif
update();
emit lockChanged();
}

void QgsLayoutItem::paint( QPainter *painter, const QStyleOptionGraphicsItem *itemStyle, QWidget * )
{
if ( !painter || !painter->device() )
Expand Down
24 changes: 24 additions & 0 deletions src/core/layout/qgslayoutitem.h
Expand Up @@ -61,6 +61,7 @@ class CORE_EXPORT QgsLayoutItem : public QgsLayoutObject, public QGraphicsRectIt
#endif

Q_OBJECT
Q_PROPERTY( bool locked READ isLocked WRITE setLocked NOTIFY lockChanged )

public:

Expand Down Expand Up @@ -122,6 +123,20 @@ class CORE_EXPORT QgsLayoutItem : public QgsLayoutObject, public QGraphicsRectIt
*/
virtual void setId( const QString &id );

/**
* Sets whether the item is \a locked, preventing mouse interactions with the item.
* \see isLocked()
* \see lockChanged()
*/
void setLocked( const bool locked );

/**
* Returns true if the item is locked, and cannot be interacted with using the mouse.
* \see setLocked()
* \see lockChanged()
*/
bool isLocked() const { return mIsLocked; }

/**
* Handles preparing a paint surface for the layout item and painting the item's
* content. Derived classes must not override this method, but instead implement
Expand Down Expand Up @@ -374,6 +389,13 @@ class CORE_EXPORT QgsLayoutItem : public QgsLayoutObject, public QGraphicsRectIt
*/
void frameChanged();

/**
* Emitted if the item's lock status changes.
* \see isLocked()
* \see setLocked()
*/
void lockChanged();

protected:

/**
Expand Down Expand Up @@ -506,6 +528,8 @@ class CORE_EXPORT QgsLayoutItem : public QgsLayoutObject, public QGraphicsRectIt
QImage mItemCachedImage;
double mItemCacheDpi = -1;

bool mIsLocked = false;

//! True if item has a frame
bool mFrame = false;
//! Item frame color
Expand Down
26 changes: 25 additions & 1 deletion tests/src/python/test_qgslayout.py
Expand Up @@ -25,7 +25,8 @@
QgsLayoutPageCollection,
QgsLayoutMeasurement,
QgsFillSymbol,
QgsReadWriteContext)
QgsReadWriteContext,
QgsLayoutItemMap)
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 @@ -78,6 +79,29 @@ def testReadWriteXml(self):
self.assertEqual(l2.guides().guidesOnPage(0)[0].position().units(), QgsUnitTypes.LayoutCentimeters)
self.assertEqual(l2.snapper().snapTolerance(), 7)

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

# add some items
item1 = QgsLayoutItemMap(l)
l.addItem(item1)
item2 = QgsLayoutItemMap(l)
l.addItem(item2)
item3 = QgsLayoutItemMap(l)
l.addItem(item3)

self.assertFalse(l.selectedLayoutItems())
item1.setSelected(True)
self.assertEqual(set(l.selectedLayoutItems()), set([item1]))
item2.setSelected(True)
self.assertEqual(set(l.selectedLayoutItems()), set([item1, item2]))
item3.setSelected(True)
self.assertEqual(set(l.selectedLayoutItems()), set([item1, item2, item3]))
item3.setLocked(True)
self.assertEqual(set(l.selectedLayoutItems(False)), set([item1, item2]))
self.assertEqual(set(l.selectedLayoutItems(True)), set([item1, item2, item3]))


if __name__ == '__main__':
unittest.main()
29 changes: 29 additions & 0 deletions tests/src/python/test_qgslayoutitem.py
Expand Up @@ -22,6 +22,7 @@
QgsLayoutMeasurement,
QgsUnitTypes)
from qgis.PyQt.QtGui import QColor
from qgis.PyQt.QtTest import QSignalSpy


start_app()
Expand Down Expand Up @@ -72,6 +73,34 @@ def testDataDefinedBackgroundColor(self):
self.assertEqual(item.backgroundColor(), QColor(255, 0, 0)) # should not change
self.assertEqual(item.brush().color().name(), QColor(0, 0, 255).name())

def testSelected(self):
"""
Ensure that items are selectable
"""
layout = QgsLayout(QgsProject.instance())
item = QgsLayoutItemMap(layout)
item.setSelected(True)
self.assertTrue(item.isSelected())
item.setSelected(False)
self.assertFalse(item.isSelected())

def testLocked(self):
layout = QgsLayout(QgsProject.instance())
item = QgsLayoutItemMap(layout)

lock_changed_spy = QSignalSpy(item.lockChanged)
item.setLocked(True)
self.assertTrue(item.isLocked())
self.assertEqual(len(lock_changed_spy), 1)
item.setLocked(True)
self.assertEqual(len(lock_changed_spy), 1)

item.setLocked(False)
self.assertFalse(item.isLocked())
self.assertEqual(len(lock_changed_spy), 2)
item.setLocked(False)
self.assertEqual(len(lock_changed_spy), 2)


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

0 comments on commit 0b18829

Please sign in to comment.