New class QgsLayoutGuideCollection for storing sets of layout snap guides

The class is a QAbstractListModel subclass, making it easy
for creation of different views showing the guides in a layout.

Additionally, a QSortFilterProxyModel (QgsLayoutGuideProxyModel)
has been created to filter QgsLayoutGuideCollection to guides
with matching page numbers and orientations.
This commit is contained in:
Nyall Dawson 2017-07-25 20:02:38 +10:00
parent fa5eb4e5ad
commit f5126b0abd
7 changed files with 544 additions and 13 deletions

View File

@ -159,6 +159,13 @@ class QgsLayout : QGraphicsScene, QgsExpressionContextGenerator
%End
QgsLayoutGuideCollection &guides();
%Docstring
Returns a reference to the layout's guide collection, which manages page snap guides.
:rtype: QgsLayoutGuideCollection
%End
virtual QgsExpressionContext createExpressionContext() const;
%Docstring

View File

@ -26,7 +26,34 @@ class QgsLayoutGuide : QObject
Vertical,
};
QgsLayoutGuide( QgsLayout *layout, Orientation orientation, const QgsLayoutMeasurement &position );
QgsLayoutGuide( Orientation orientation, const QgsLayoutMeasurement &position );
%Docstring
Constructor for a new guide with the specified ``orientation`` and
initial ``position``.
A layout must be set by calling setLayout() before the guide can be used.
Adding the guide to a QgsLayoutGuideCollection will automatically set
the corresponding layout for you.
%End
QgsLayout *layout() const;
%Docstring
Returns the layout the guide belongs to.
.. seealso:: setLayout()
:rtype: QgsLayout
%End
void setLayout( QgsLayout *layout );
%Docstring
Sets the ``layout`` the guide belongs to.
.. note::
Adding the guide to a QgsLayoutGuideCollection will automatically set
the corresponding layout for you.
.. seealso:: layout()
%End
Orientation orientation() const;
%Docstring
@ -85,9 +112,103 @@ class QgsLayoutGuide : QObject
:rtype: QGraphicsLineItem
%End
double layoutPosition() const;
%Docstring
Returns the guide's position in absolute layout units.
:rtype: float
%End
signals:
void positionChanged();
%Docstring
Emitted when the guide's position is changed.
%End
};
class QgsLayoutGuideCollection : QAbstractListModel
{
%Docstring
Stores and manages the snap guides used by a layout.
.. versionadded:: 3.0
%End
%TypeHeaderCode
#include "qgslayoutguidecollection.h"
%End
public:
enum Roles
{
OrientationRole,
PositionRole,
UnitsRole,
PageRole,
};
QgsLayoutGuideCollection( QgsLayout *layout );
%Docstring
Constructor for QgsLayoutGuideCollection belonging to the specified layout.
%End
~QgsLayoutGuideCollection();
virtual int rowCount( const QModelIndex & ) const;
virtual QVariant data( const QModelIndex &index, int role ) const;
virtual bool setData( const QModelIndex &index, const QVariant &value, int role );
virtual Qt::ItemFlags flags( const QModelIndex &index ) const;
void addGuide( QgsLayoutGuide *guide /Transfer/ );
%Docstring
Adds a ``guide`` to the collection. Ownership of the guide is transferred to the
collection, and the guide will automatically have the correct layout
set.
%End
void update();
%Docstring
Updates the position (and visibility) of all guide line items.
%End
QList< QgsLayoutGuide * > guides( QgsLayoutGuide::Orientation orientation );
%Docstring
Returns the list of guides contained in the collection with the specified
``orientation``.
:rtype: list of QgsLayoutGuide
%End
};
class QgsLayoutGuideProxyModel : QSortFilterProxyModel
{
%Docstring
Filters QgsLayoutGuideCollection models to guides of a single orientation (horizontal or vertical).
.. versionadded:: 3.0
%End
%TypeHeaderCode
#include "qgslayoutguidecollection.h"
%End
public:
explicit QgsLayoutGuideProxyModel( QObject *parent /TransferThis/, QgsLayoutGuide::Orientation orientation, int page );
%Docstring
Constructor for QgsLayoutGuideProxyModel, filtered to guides of the specified ``orientation`` and ``page`` only.
Page numbers begin at 0.
%End
virtual bool filterAcceptsRow( int sourceRow, const QModelIndex &sourceParent ) const;
virtual bool lessThan( const QModelIndex &left, const QModelIndex &right ) const;
};
/************************************************************************
* This file has been generated automatically from *

View File

@ -16,11 +16,13 @@
#include "qgslayout.h"
#include "qgslayoutpagecollection.h"
#include "qgslayoutguidecollection.h"
QgsLayout::QgsLayout( QgsProject *project )
: QGraphicsScene()
, mProject( project )
, mSnapper( QgsLayoutSnapper( this ) )
, mGuideCollection( new QgsLayoutGuideCollection( this ) )
, mPageCollection( new QgsLayoutPageCollection( this ) )
{
// just to make sure - this should be the default, but maybe it'll change in some future Qt version...
@ -70,6 +72,16 @@ QgsLayoutPoint QgsLayout::convertFromLayoutUnits( const QPointF &point, const Qg
return mContext.measurementConverter().convert( QgsLayoutPoint( point.x(), point.y(), mUnits ), unit );
}
QgsLayoutGuideCollection &QgsLayout::guides()
{
return *mGuideCollection;
}
const QgsLayoutGuideCollection &QgsLayout::guides() const
{
return *mGuideCollection;
}
QgsExpressionContext QgsLayout::createExpressionContext() const
{
QgsExpressionContext context = QgsExpressionContext();

View File

@ -23,6 +23,7 @@
#include "qgsexpressioncontextgenerator.h"
#include "qgslayoutpagecollection.h"
#include "qgslayoutgridsettings.h"
#include "qgslayoutguidecollection.h"
class QgsLayoutItemMap;
@ -182,6 +183,16 @@ class CORE_EXPORT QgsLayout : public QGraphicsScene, public QgsExpressionContext
*/
SIP_SKIP const QgsLayoutGridSettings &gridSettings() const { return mGridSettings; }
/**
* Returns a reference to the layout's guide collection, which manages page snap guides.
*/
QgsLayoutGuideCollection &guides();
/**
* Returns a reference to the layout's guide collection, which manages page snap guides.
*/
SIP_SKIP const QgsLayoutGuideCollection &guides() const;
/**
* Creates an expression context relating to the layout's current state. The context includes
* scopes for global, project, layout and layout context properties.
@ -292,6 +303,7 @@ class CORE_EXPORT QgsLayout : public QGraphicsScene, public QgsExpressionContext
QgsLayoutContext mContext;
QgsLayoutSnapper mSnapper;
QgsLayoutGridSettings mGridSettings;
std::unique_ptr< QgsLayoutGuideCollection > mGuideCollection;
std::unique_ptr< QgsLayoutPageCollection > mPageCollection;

View File

@ -23,17 +23,14 @@
// QgsLayoutGuide
//
QgsLayoutGuide::QgsLayoutGuide( QgsLayout *layout, Orientation orientation, const QgsLayoutMeasurement &position )
: QObject( layout )
QgsLayoutGuide::QgsLayoutGuide( Orientation orientation, const QgsLayoutMeasurement &position )
: QObject( nullptr )
, mOrientation( orientation )
, mPosition( position )
, mLayout( layout )
, mLineItem( new QGraphicsLineItem() )
{
mLineItem->hide();
mLineItem->setZValue( QgsLayout::ZGuide );
mLayout->addItem( mLineItem.get() );
QPen linePen( Qt::SolidLine );
linePen.setColor( Qt::red );
// use a pen width of 0, since this activates a cosmetic pen
@ -67,6 +64,9 @@ void QgsLayoutGuide::setPage( int page )
void QgsLayoutGuide::update()
{
if ( !mLayout )
return;
// first find matching page
if ( mPage >= mLayout->pageCollection()->pageCount() )
{
@ -125,7 +125,181 @@ double QgsLayoutGuide::layoutPosition() const
return -999; // avoid warning
}
QgsLayout *QgsLayoutGuide::layout() const
{
return mLayout;
}
void QgsLayoutGuide::setLayout( QgsLayout *layout )
{
mLayout = layout;
mLayout->addItem( mLineItem.get() );
update();
}
QgsLayoutGuide::Orientation QgsLayoutGuide::orientation() const
{
return mOrientation;
}
//
// QgsLayoutGuideCollection
//
QgsLayoutGuideCollection::QgsLayoutGuideCollection( QgsLayout *layout )
: QAbstractListModel( layout )
, mLayout( layout )
{
}
QgsLayoutGuideCollection::~QgsLayoutGuideCollection()
{
qDeleteAll( mGuides );
}
int QgsLayoutGuideCollection::rowCount( const QModelIndex & ) const
{
return mGuides.count();
}
QVariant QgsLayoutGuideCollection::data( const QModelIndex &index, int role ) const
{
if ( !index.isValid() )
return QVariant();
if ( index.row() >= mGuides.count() || index.row() < 0 )
return QVariant();
QgsLayoutGuide *guide = mGuides.at( index.row() );
switch ( role )
{
case Qt::DisplayRole:
case Qt::EditRole:
{
return guide->position().length();
}
case Qt::TextAlignmentRole:
return QVariant( Qt::AlignRight | Qt::AlignVCenter );
case OrientationRole:
return guide->orientation();
case PositionRole:
return guide->position().length();
case UnitsRole:
return guide->position().units();
case PageRole:
return guide->page();
case LayoutPositionRole:
return guide->layoutPosition();
default:
return QVariant();
}
}
bool QgsLayoutGuideCollection::setData( const QModelIndex &index, const QVariant &value, int role )
{
if ( !index.isValid() )
return false;
if ( index.row() >= mGuides.count() || index.row() < 0 )
return false;
QgsLayoutGuide *guide = mGuides.at( index.row() );
if ( role == Qt::EditRole )
{
bool ok = false;
double newPos = value.toDouble( &ok );
if ( !ok )
return false;
QgsLayoutMeasurement m = guide->position();
m.setLength( newPos );
guide->setPosition( m );
guide->update();
return true;
}
return false;
}
Qt::ItemFlags QgsLayoutGuideCollection::flags( const QModelIndex &index ) const
{
if ( !index.isValid() )
return Qt::ItemIsEnabled | Qt::ItemIsSelectable;
return Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsEditable;
}
void QgsLayoutGuideCollection::addGuide( QgsLayoutGuide *guide )
{
guide->setLayout( mLayout );
beginInsertRows( QModelIndex(), mGuides.count(), mGuides.count() );
mGuides.append( guide );
endInsertRows();
QModelIndex index = createIndex( mGuides.length() - 1, 0 );
connect( guide, &QgsLayoutGuide::positionChanged, this, [ this, index ]
{
emit dataChanged( index, index );
} );
}
void QgsLayoutGuideCollection::update()
{
Q_FOREACH ( QgsLayoutGuide *guide, mGuides )
{
guide->update();
}
}
QList<QgsLayoutGuide *> QgsLayoutGuideCollection::guides( QgsLayoutGuide::Orientation orientation )
{
QList<QgsLayoutGuide *> res;
Q_FOREACH ( QgsLayoutGuide *guide, mGuides )
{
if ( guide->orientation() == orientation && guide->item()->isVisible() )
res << guide;
}
return res;
}
//
// QgsLayoutGuideProxyModel
//
QgsLayoutGuideProxyModel::QgsLayoutGuideProxyModel( QObject *parent, QgsLayoutGuide::Orientation orientation, int page )
: QSortFilterProxyModel( parent )
, mOrientation( orientation )
, mPage( page )
{
setDynamicSortFilter( true );
sort( 0 );
}
bool QgsLayoutGuideProxyModel::filterAcceptsRow( int source_row, const QModelIndex &source_parent ) const
{
QModelIndex index = sourceModel()->index( source_row, 0, source_parent );
QgsLayoutGuide::Orientation orientation = static_cast< QgsLayoutGuide::Orientation>( sourceModel()->data( index, QgsLayoutGuideCollection::OrientationRole ).toInt() );
if ( orientation != mOrientation )
return false;
int page = sourceModel()->data( index, QgsLayoutGuideCollection::PageRole ).toInt();
return page == mPage;
}
bool QgsLayoutGuideProxyModel::lessThan( const QModelIndex &left, const QModelIndex &right ) const
{
double leftPos = sourceModel()->data( left, QgsLayoutGuideCollection::LayoutPositionRole ).toDouble();
double rightPos = sourceModel()->data( right, QgsLayoutGuideCollection::LayoutPositionRole ).toDouble();
return leftPos < rightPos;
}

View File

@ -20,10 +20,12 @@
#include "qgslayoutmeasurement.h"
#include "qgslayoutpoint.h"
#include <QPen>
#include <QAbstractListModel>
#include <QSortFilterProxyModel>
#include <QGraphicsLineItem>
#include <memory>
class QgsLayout;
class QGraphicsLineItem;
/**
* \ingroup core
@ -45,7 +47,31 @@ class CORE_EXPORT QgsLayoutGuide : public QObject
Vertical, //!< Vertical guide
};
QgsLayoutGuide( QgsLayout *layout, Orientation orientation, const QgsLayoutMeasurement &position );
/**
* Constructor for a new guide with the specified \a orientation and
* initial \a position.
*
* A layout must be set by calling setLayout() before the guide can be used.
* Adding the guide to a QgsLayoutGuideCollection will automatically set
* the corresponding layout for you.
*/
QgsLayoutGuide( Orientation orientation, const QgsLayoutMeasurement &position );
/**
* Returns the layout the guide belongs to.
* \see setLayout()
*/
QgsLayout *layout() const;
/**
* Sets the \a layout the guide belongs to.
*
* \note Adding the guide to a QgsLayoutGuideCollection will automatically set
* the corresponding layout for you.
*
* \see layout()
*/
void setLayout( QgsLayout *layout );
/**
* Returns the guide's orientation.
@ -125,9 +151,96 @@ class CORE_EXPORT QgsLayoutGuide : public QObject
QgsLayout *mLayout = nullptr;
//! Line item used in scene for guide
std::shared_ptr< QGraphicsLineItem > mLineItem;
std::unique_ptr< QGraphicsLineItem > mLineItem;
};
/**
* \ingroup core
* \class QgsLayoutGuideCollection
* \brief Stores and manages the snap guides used by a layout.
* \since QGIS 3.0
*/
class CORE_EXPORT QgsLayoutGuideCollection : public QAbstractListModel
{
Q_OBJECT
public:
//! Model roles
enum Roles
{
OrientationRole = Qt::UserRole, //!< Guide orientation role
PositionRole, //!< Guide position role
UnitsRole, //!< Guide position units role
PageRole, //!< Guide page role
};
/**
* Constructor for QgsLayoutGuideCollection belonging to the specified layout.
*/
QgsLayoutGuideCollection( QgsLayout *layout );
~QgsLayoutGuideCollection();
int rowCount( const QModelIndex & ) const override;
QVariant data( const QModelIndex &index, int role ) const override;
bool setData( const QModelIndex &index, const QVariant &value, int role ) override;
Qt::ItemFlags flags( const QModelIndex &index ) const override;
/**
* Adds a \a guide to the collection. Ownership of the guide is transferred to the
* collection, and the guide will automatically have the correct layout
* set.
*/
void addGuide( QgsLayoutGuide *guide SIP_TRANSFER );
/**
* Updates the position (and visibility) of all guide line items.
*/
void update();
/**
* Returns the list of guides contained in the collection with the specified
* \a orientation.
*/
QList< QgsLayoutGuide * > guides( QgsLayoutGuide::Orientation orientation );
private:
QgsLayout *mLayout = nullptr;
QList< QgsLayoutGuide * > mGuides;
};
/**
* \ingroup core
* \class QgsLayoutGuideProxyModel
* \brief Filters QgsLayoutGuideCollection models to guides of a single orientation (horizontal or vertical).
* \since QGIS 3.0
*/
class CORE_EXPORT QgsLayoutGuideProxyModel : public QSortFilterProxyModel
{
Q_OBJECT
public:
/**
* Constructor for QgsLayoutGuideProxyModel, filtered to guides of the specified \a orientation and \a page only.
*
* Page numbers begin at 0.
*/
explicit QgsLayoutGuideProxyModel( QObject *parent SIP_TRANSFERTHIS, QgsLayoutGuide::Orientation orientation, int page );
bool filterAcceptsRow( int sourceRow, const QModelIndex &sourceParent ) const override;
bool lessThan( const QModelIndex &left, const QModelIndex &right ) const override;
private:
QgsLayoutGuide::Orientation mOrientation = QgsLayoutGuide::Horizontal;
int mPage = 0;
};
#endif //QGSLAYOUTGUIDECOLLECTION_H

View File

@ -20,7 +20,10 @@ from qgis.core import (QgsProject,
QgsLayoutMeasurement,
QgsUnitTypes,
QgsLayoutPoint,
QgsLayoutItemPage)
QgsLayoutItemPage,
QgsLayoutGuideCollection,
QgsLayoutGuideProxyModel)
from qgis.PyQt.QtCore import (QModelIndex)
from qgis.PyQt.QtGui import (QPen,
QColor)
from qgis.PyQt.QtTest import QSignalSpy
@ -36,11 +39,14 @@ class TestQgsLayoutGuide(unittest.TestCase):
p = QgsProject()
l = QgsLayout(p)
l.initializeDefaults() # add a page
g = QgsLayoutGuide(l, QgsLayoutGuide.Horizontal, QgsLayoutMeasurement(5, QgsUnitTypes.LayoutCentimeters))
g = QgsLayoutGuide(QgsLayoutGuide.Horizontal, QgsLayoutMeasurement(5, QgsUnitTypes.LayoutCentimeters))
self.assertEqual(g.orientation(), QgsLayoutGuide.Horizontal)
self.assertEqual(g.position().length(), 5.0)
self.assertEqual(g.position().units(), QgsUnitTypes.LayoutCentimeters)
g.setLayout(l)
self.assertEqual(g.layout(), l)
position_changed_spy = QSignalSpy(g.positionChanged)
g.setPosition(QgsLayoutMeasurement(15, QgsUnitTypes.LayoutInches))
self.assertEqual(g.position().length(), 15.0)
@ -54,7 +60,8 @@ class TestQgsLayoutGuide(unittest.TestCase):
p = QgsProject()
l = QgsLayout(p)
l.initializeDefaults() # add a page
g = QgsLayoutGuide(l, QgsLayoutGuide.Horizontal, QgsLayoutMeasurement(5, QgsUnitTypes.LayoutCentimeters))
g = QgsLayoutGuide(QgsLayoutGuide.Horizontal, QgsLayoutMeasurement(5, QgsUnitTypes.LayoutCentimeters))
g.setLayout(l)
g.update()
self.assertTrue(g.item().isVisible())
@ -74,7 +81,8 @@ class TestQgsLayoutGuide(unittest.TestCase):
self.assertEqual(g.layoutPosition(), 15)
# vertical guide
g2 = QgsLayoutGuide(l, QgsLayoutGuide.Vertical, QgsLayoutMeasurement(5, QgsUnitTypes.LayoutCentimeters))
g2 = QgsLayoutGuide(QgsLayoutGuide.Vertical, QgsLayoutMeasurement(5, QgsUnitTypes.LayoutCentimeters))
g2.setLayout(l)
g2.update()
self.assertTrue(g2.item().isVisible())
self.assertEqual(g2.item().line().x1(), 50)
@ -96,6 +104,90 @@ class TestQgsLayoutGuide(unittest.TestCase):
g.update()
self.assertFalse(g.item().isVisible())
def testCollection(self):
p = QgsProject()
l = QgsLayout(p)
l.initializeDefaults() # add a page
guides = l.guides()
# no guides initially
self.assertEqual(guides.rowCount(QModelIndex()), 0)
self.assertFalse(guides.data(QModelIndex(), QgsLayoutGuideCollection.OrientationRole))
self.assertFalse(guides.guides(QgsLayoutGuide.Horizontal))
self.assertFalse(guides.guides(QgsLayoutGuide.Vertical))
# add a guide
g1 = QgsLayoutGuide(QgsLayoutGuide.Horizontal, QgsLayoutMeasurement(5, QgsUnitTypes.LayoutCentimeters))
guides.addGuide(g1)
self.assertEqual(guides.rowCount(QModelIndex()), 1)
self.assertEqual(guides.data(guides.index(0, 0), QgsLayoutGuideCollection.OrientationRole), QgsLayoutGuide.Horizontal)
self.assertEqual(guides.data(guides.index(0, 0), QgsLayoutGuideCollection.PositionRole), 5)
self.assertEqual(guides.data(guides.index(0, 0), QgsLayoutGuideCollection.UnitsRole), QgsUnitTypes.LayoutCentimeters)
self.assertEqual(guides.data(guides.index(0, 0), QgsLayoutGuideCollection.PageRole), 0)
self.assertEqual(guides.guides(QgsLayoutGuide.Horizontal), [g1])
self.assertFalse(guides.guides(QgsLayoutGuide.Vertical))
g2 = QgsLayoutGuide(QgsLayoutGuide.Horizontal, QgsLayoutMeasurement(15))
guides.addGuide(g2)
self.assertEqual(guides.rowCount(QModelIndex()), 2)
self.assertEqual(guides.data(guides.index(1, 0), QgsLayoutGuideCollection.OrientationRole), QgsLayoutGuide.Horizontal)
self.assertEqual(guides.data(guides.index(1, 0), QgsLayoutGuideCollection.PositionRole), 15)
self.assertEqual(guides.data(guides.index(1, 0), QgsLayoutGuideCollection.UnitsRole), QgsUnitTypes.LayoutMillimeters)
self.assertEqual(guides.data(guides.index(1, 0), QgsLayoutGuideCollection.PageRole), 0)
self.assertEqual(guides.guides(QgsLayoutGuide.Horizontal), [g1, g2])
self.assertFalse(guides.guides(QgsLayoutGuide.Vertical))
page2 = QgsLayoutItemPage(l)
page2.setPageSize('A3')
l.pageCollection().addPage(page2)
g3 = QgsLayoutGuide(QgsLayoutGuide.Vertical, QgsLayoutMeasurement(35))
g3.setPage(1)
guides.addGuide(g3)
self.assertEqual(guides.rowCount(QModelIndex()), 3)
self.assertEqual(guides.data(guides.index(2, 0), QgsLayoutGuideCollection.OrientationRole), QgsLayoutGuide.Vertical)
self.assertEqual(guides.data(guides.index(2, 0), QgsLayoutGuideCollection.PositionRole), 35)
self.assertEqual(guides.data(guides.index(2, 0), QgsLayoutGuideCollection.UnitsRole), QgsUnitTypes.LayoutMillimeters)
self.assertEqual(guides.data(guides.index(2, 0), QgsLayoutGuideCollection.PageRole), 1)
self.assertEqual(guides.guides(QgsLayoutGuide.Horizontal), [g1, g2])
self.assertEqual(guides.guides(QgsLayoutGuide.Vertical), [g3])
def testQgsLayoutGuideProxyModel(self):
p = QgsProject()
l = QgsLayout(p)
l.initializeDefaults() # add a page
page2 = QgsLayoutItemPage(l)
page2.setPageSize('A3')
l.pageCollection().addPage(page2)
guides = l.guides()
hoz_filter = QgsLayoutGuideProxyModel(None, QgsLayoutGuide.Horizontal, 0)
hoz_filter.setSourceModel(guides)
hoz_page_1_filter = QgsLayoutGuideProxyModel(None, QgsLayoutGuide.Horizontal, 1)
hoz_page_1_filter.setSourceModel(guides)
vert_filter = QgsLayoutGuideProxyModel(None, QgsLayoutGuide.Vertical, 0)
vert_filter.setSourceModel(guides)
# no guides initially
self.assertEqual(hoz_filter.rowCount(QModelIndex()), 0)
self.assertEqual(hoz_page_1_filter.rowCount(QModelIndex()), 0)
self.assertEqual(vert_filter.rowCount(QModelIndex()), 0)
# add some guides
g1 = QgsLayoutGuide(QgsLayoutGuide.Horizontal, QgsLayoutMeasurement(5, QgsUnitTypes.LayoutCentimeters))
guides.addGuide(g1)
g2 = QgsLayoutGuide(QgsLayoutGuide.Horizontal, QgsLayoutMeasurement(15))
g2.setPage(1)
guides.addGuide(g2)
g3 = QgsLayoutGuide(QgsLayoutGuide.Vertical, QgsLayoutMeasurement(35))
guides.addGuide(g3)
self.assertEqual(hoz_filter.rowCount(QModelIndex()), 1)
self.assertEqual(hoz_filter.data(hoz_filter.index(0, 0), QgsLayoutGuideCollection.PositionRole), 5)
self.assertEqual(hoz_page_1_filter.rowCount(QModelIndex()), 1)
self.assertEqual(hoz_page_1_filter.data(hoz_page_1_filter.index(0, 0), QgsLayoutGuideCollection.PositionRole), 15)
self.assertEqual(vert_filter.rowCount(QModelIndex()), 1)
self.assertEqual(vert_filter.data(vert_filter.index(0, 0), QgsLayoutGuideCollection.PositionRole), 35)
if __name__ == '__main__':
unittest.main()