Move layout manager model from app to core and add unit tests

This commit is contained in:
Nyall Dawson 2019-03-11 13:21:19 +10:00
parent 0b270f98fb
commit cba22770dc
8 changed files with 729 additions and 198 deletions

View File

@ -0,0 +1,3 @@
# The following has been generated automatically from src/core/layout/qgslayoutmanager.h
QgsLayoutManagerProxyModel.Filters.baseClass = QgsLayoutManagerProxyModel
Filters = QgsLayoutManagerProxyModel # dirty hack since SIP seems to introduce the flags in module

View File

@ -9,7 +9,6 @@
class QgsLayoutManager : QObject
{
%Docstring
@ -145,6 +144,122 @@ Emitted when a layout is renamed
};
class QgsLayoutManagerModel : QAbstractListModel
{
%Docstring
List model representing the print layouts and reports available in a
layout manager.
.. versionadded:: 3.8
%End
%TypeHeaderCode
#include "qgslayoutmanager.h"
%End
public:
enum Role
{
LayoutRole,
};
explicit QgsLayoutManagerModel( QgsLayoutManager *manager, QObject *parent /TransferThis/ = 0 );
%Docstring
Constructor for QgsLayoutManagerModel, showing the layouts from the specified ``manager``.
%End
virtual int rowCount( const QModelIndex &parent ) const;
virtual QVariant data( const QModelIndex &index, int role ) const;
virtual bool setData( const QModelIndex &index, const QVariant &value, int role = Qt::EditRole );
virtual Qt::ItemFlags flags( const QModelIndex &index ) const;
QgsMasterLayoutInterface *layoutFromIndex( const QModelIndex &index ) const;
%Docstring
Returns the layout at the corresponding ``index``.
.. seealso:: :py:func:`indexFromLayout`
%End
QModelIndex indexFromLayout( QgsMasterLayoutInterface *layout ) const;
%Docstring
Returns the model index corresponding to a ``layout``.
.. seealso:: :py:func:`layoutFromIndex`
%End
void setAllowEmptyLayout( bool allowEmpty );
%Docstring
Sets whether an optional empty layout ("not set") option is present in the model.
.. seealso:: :py:func:`allowEmptyLayout`
%End
bool allowEmptyLayout() const;
%Docstring
Returns ``True`` if the model allows the empty layout ("not set") choice.
.. seealso:: :py:func:`setAllowEmptyLayout`
%End
};
class QgsLayoutManagerProxyModel : QSortFilterProxyModel
{
%Docstring
QSortFilterProxyModel subclass for QgsLayoutManagerModel
.. versionadded:: 3.8
%End
%TypeHeaderCode
#include "qgslayoutmanager.h"
%End
public:
enum Filter
{
FilterPrintLayouts,
FilterReports,
};
typedef QFlags<QgsLayoutManagerProxyModel::Filter> Filters;
explicit QgsLayoutManagerProxyModel( QObject *parent /TransferThis/ = 0 );
%Docstring
Constructor for QgsLayoutManagerProxyModel.
%End
virtual bool lessThan( const QModelIndex &left, const QModelIndex &right ) const;
virtual bool filterAcceptsRow( int sourceRow, const QModelIndex &sourceParent ) const;
QgsLayoutManagerProxyModel::Filters filters() const;
%Docstring
Returns the current filters used for filtering available layouts.
.. seealso:: :py:func:`setFilters`
%End
void setFilters( QgsLayoutManagerProxyModel::Filters filters );
%Docstring
Sets the current ``filters`` used for filtering available layouts.
.. seealso:: :py:func:`filters`
%End
};
QFlags<QgsLayoutManagerProxyModel::Filter> operator|(QgsLayoutManagerProxyModel::Filter f1, QFlags<QgsLayoutManagerProxyModel::Filter> f2);
/************************************************************************
* This file has been generated automatically from *
* *

View File

@ -488,161 +488,4 @@ void QgsLayoutManagerDialog::showHelp()
QgsHelp::openHelp( QStringLiteral( "print_composer/overview_composer.html#the-layout-manager" ) );
}
//
// QgsLayoutManagerModel
//
QgsLayoutManagerModel::QgsLayoutManagerModel( QgsLayoutManager *manager, QObject *parent )
: QAbstractListModel( parent )
, mLayoutManager( manager )
{
connect( mLayoutManager, &QgsLayoutManager::layoutAboutToBeAdded, this, &QgsLayoutManagerModel::layoutAboutToBeAdded );
connect( mLayoutManager, &QgsLayoutManager::layoutAdded, this, &QgsLayoutManagerModel::layoutAdded );
connect( mLayoutManager, &QgsLayoutManager::layoutAboutToBeRemoved, this, &QgsLayoutManagerModel::layoutAboutToBeRemoved );
connect( mLayoutManager, &QgsLayoutManager::layoutRemoved, this, &QgsLayoutManagerModel::layoutRemoved );
connect( mLayoutManager, &QgsLayoutManager::layoutRenamed, this, &QgsLayoutManagerModel::layoutRenamed );
}
int QgsLayoutManagerModel::rowCount( const QModelIndex &parent ) const
{
Q_UNUSED( parent );
return mLayoutManager->layouts().count();
}
QVariant QgsLayoutManagerModel::data( const QModelIndex &index, int role ) const
{
if ( index.row() < 0 || index.row() >= rowCount( QModelIndex() ) )
return QVariant();
switch ( role )
{
case Qt::DisplayRole:
case Qt::ToolTipRole:
case Qt::EditRole:
return mLayoutManager->layouts().at( index.row() )->name();
case LayoutRole:
{
if ( QgsLayout *l = dynamic_cast< QgsLayout * >( mLayoutManager->layouts().at( index.row() ) ) )
return QVariant::fromValue( l );
else if ( QgsReport *r = dynamic_cast< QgsReport * >( mLayoutManager->layouts().at( index.row() ) ) )
return QVariant::fromValue( r );
else
return QVariant();
}
case Qt::DecorationRole:
{
return mLayoutManager->layouts().at( index.row() )->icon();
}
default:
return QVariant();
}
}
bool QgsLayoutManagerModel::setData( const QModelIndex &index, const QVariant &value, int role )
{
if ( !index.isValid() || role != Qt::EditRole )
{
return false;
}
if ( index.row() >= mLayoutManager->layouts().count() )
{
return false;
}
if ( value.toString().isEmpty() )
return false;
QgsMasterLayoutInterface *layout = layoutFromIndex( index );
if ( !layout )
return false;
//has name changed?
bool changed = layout->name() != value.toString();
if ( !changed )
return true;
//check if name already exists
QStringList layoutNames;
const QList< QgsMasterLayoutInterface * > layouts = QgsProject::instance()->layoutManager()->layouts();
for ( QgsMasterLayoutInterface *l : layouts )
{
layoutNames << l->name();
}
if ( layoutNames.contains( value.toString() ) )
{
//name exists!
QMessageBox::warning( nullptr, tr( "Rename Layout" ), tr( "There is already a layout named “%1”." ).arg( value.toString() ) );
return false;
}
layout->setName( value.toString() );
return true;
}
Qt::ItemFlags QgsLayoutManagerModel::flags( const QModelIndex &index ) const
{
Qt::ItemFlags flags = QAbstractListModel::flags( index );
#if 0 // double-click is now used for opening the layout
if ( index.isValid() )
{
return flags | Qt::ItemIsEditable;
}
else
{
return flags;
}
#endif
return flags;
}
QgsMasterLayoutInterface *QgsLayoutManagerModel::layoutFromIndex( const QModelIndex &index ) const
{
if ( QgsPrintLayout *l = qobject_cast< QgsPrintLayout * >( qvariant_cast<QObject *>( data( index, LayoutRole ) ) ) )
return l;
else if ( QgsReport *r = qobject_cast< QgsReport * >( qvariant_cast<QObject *>( data( index, LayoutRole ) ) ) )
return r;
else
return nullptr;
}
void QgsLayoutManagerModel::layoutAboutToBeAdded( const QString & )
{
int row = mLayoutManager->layouts().count();
beginInsertRows( QModelIndex(), row, row );
}
void QgsLayoutManagerModel::layoutAboutToBeRemoved( const QString &name )
{
QgsMasterLayoutInterface *l = mLayoutManager->layoutByName( name );
int row = mLayoutManager->layouts().indexOf( l );
if ( row >= 0 )
beginRemoveRows( QModelIndex(), row, row );
}
void QgsLayoutManagerModel::layoutAdded( const QString & )
{
endInsertRows();
}
void QgsLayoutManagerModel::layoutRemoved( const QString & )
{
endRemoveRows();
}
void QgsLayoutManagerModel::layoutRenamed( QgsMasterLayoutInterface *layout, const QString & )
{
int row = mLayoutManager->layouts().indexOf( layout );
QModelIndex index = createIndex( row, 0 );
emit dataChanged( index, index, QVector<int>() << Qt::DisplayRole );
}
QgsLayoutManagerProxyModel::QgsLayoutManagerProxyModel( QObject *parent )
: QSortFilterProxyModel( parent )
{
setDynamicSortFilter( true );
sort( 0 );
setSortCaseSensitivity( Qt::CaseInsensitive );
}

View File

@ -26,45 +26,8 @@ class QListWidgetItem;
class QgsLayoutDesignerDialog;
class QgsMasterLayoutInterface;
class QgsLayoutManager;
class QgsLayoutManagerModel : public QAbstractListModel
{
Q_OBJECT
public:
enum Role
{
LayoutRole = Qt::UserRole + 1,
};
explicit QgsLayoutManagerModel( QgsLayoutManager *manager, QObject *parent = nullptr );
int rowCount( const QModelIndex &parent ) const override;
QVariant data( const QModelIndex &index, int role ) const override;
bool setData( const QModelIndex &index, const QVariant &value, int role = Qt::EditRole ) override;
Qt::ItemFlags flags( const QModelIndex &index ) const override;
QgsMasterLayoutInterface *layoutFromIndex( const QModelIndex &index ) const;
private slots:
void layoutAboutToBeAdded( const QString &name );
void layoutAboutToBeRemoved( const QString &name );
void layoutAdded( const QString &name );
void layoutRemoved( const QString &name );
void layoutRenamed( QgsMasterLayoutInterface *layout, const QString &newName );
private:
QgsLayoutManager *mLayoutManager = nullptr;
};
class QgsLayoutManagerProxyModel : public QSortFilterProxyModel
{
Q_OBJECT
public:
explicit QgsLayoutManagerProxyModel( QObject *parent );
};
class QgsLayoutManagerModel;
class QgsLayoutManagerProxyModel;
/**
* A dialog that allows management of layouts within a project.

View File

@ -22,6 +22,7 @@
#include "qgsreport.h"
#include "qgscompositionconverter.h"
#include "qgsreadwritecontext.h"
#include <QMessageBox>
QgsLayoutManager::QgsLayoutManager( QgsProject *project )
: QObject( project )
@ -302,3 +303,260 @@ QString QgsLayoutManager::generateUniqueTitle( QgsMasterLayoutInterface::Type ty
return name;
}
//
// QgsLayoutManagerModel
//
QgsLayoutManagerModel::QgsLayoutManagerModel( QgsLayoutManager *manager, QObject *parent )
: QAbstractListModel( parent )
, mLayoutManager( manager )
{
connect( mLayoutManager, &QgsLayoutManager::layoutAboutToBeAdded, this, &QgsLayoutManagerModel::layoutAboutToBeAdded );
connect( mLayoutManager, &QgsLayoutManager::layoutAdded, this, &QgsLayoutManagerModel::layoutAdded );
connect( mLayoutManager, &QgsLayoutManager::layoutAboutToBeRemoved, this, &QgsLayoutManagerModel::layoutAboutToBeRemoved );
connect( mLayoutManager, &QgsLayoutManager::layoutRemoved, this, &QgsLayoutManagerModel::layoutRemoved );
connect( mLayoutManager, &QgsLayoutManager::layoutRenamed, this, &QgsLayoutManagerModel::layoutRenamed );
}
int QgsLayoutManagerModel::rowCount( const QModelIndex &parent ) const
{
Q_UNUSED( parent );
return mLayoutManager->layouts().count() + ( mAllowEmpty ? 1 : 0 );
}
QVariant QgsLayoutManagerModel::data( const QModelIndex &index, int role ) const
{
if ( index.row() < 0 || index.row() >= rowCount( QModelIndex() ) )
return QVariant();
const bool isEmpty = index.row() == 0 && mAllowEmpty;
const int layoutRow = mAllowEmpty ? index.row() - 1 : index.row();
switch ( role )
{
case Qt::DisplayRole:
case Qt::ToolTipRole:
case Qt::EditRole:
return !isEmpty ? mLayoutManager->layouts().at( layoutRow )->name() : QVariant();
case LayoutRole:
{
if ( isEmpty )
return QVariant();
else if ( QgsLayout *l = dynamic_cast< QgsLayout * >( mLayoutManager->layouts().at( layoutRow ) ) )
return QVariant::fromValue( l );
else if ( QgsReport *r = dynamic_cast< QgsReport * >( mLayoutManager->layouts().at( layoutRow ) ) )
return QVariant::fromValue( r );
else
return QVariant();
}
case Qt::DecorationRole:
{
return isEmpty ? QIcon() : mLayoutManager->layouts().at( layoutRow )->icon();
}
default:
return QVariant();
}
}
bool QgsLayoutManagerModel::setData( const QModelIndex &index, const QVariant &value, int role )
{
if ( !index.isValid() || role != Qt::EditRole )
{
return false;
}
if ( index.row() >= mLayoutManager->layouts().count() )
{
return false;
}
if ( index.row() == 0 && mAllowEmpty )
return false;
if ( value.toString().isEmpty() )
return false;
QgsMasterLayoutInterface *layout = layoutFromIndex( index );
if ( !layout )
return false;
//has name changed?
bool changed = layout->name() != value.toString();
if ( !changed )
return true;
//check if name already exists
QStringList layoutNames;
const QList< QgsMasterLayoutInterface * > layouts = QgsProject::instance()->layoutManager()->layouts();
for ( QgsMasterLayoutInterface *l : layouts )
{
layoutNames << l->name();
}
if ( layoutNames.contains( value.toString() ) )
{
//name exists!
QMessageBox::warning( nullptr, tr( "Rename Layout" ), tr( "There is already a layout named “%1”." ).arg( value.toString() ) );
return false;
}
layout->setName( value.toString() );
return true;
}
Qt::ItemFlags QgsLayoutManagerModel::flags( const QModelIndex &index ) const
{
Qt::ItemFlags flags = QAbstractListModel::flags( index );
#if 0 // double-click is now used for opening the layout
if ( index.isValid() )
{
return flags | Qt::ItemIsEditable;
}
else
{
return flags;
}
#endif
return flags;
}
QgsMasterLayoutInterface *QgsLayoutManagerModel::layoutFromIndex( const QModelIndex &index ) const
{
if ( index.row() == 0 && mAllowEmpty )
return nullptr;
if ( QgsPrintLayout *l = qobject_cast< QgsPrintLayout * >( qvariant_cast<QObject *>( data( index, LayoutRole ) ) ) )
return l;
else if ( QgsReport *r = qobject_cast< QgsReport * >( qvariant_cast<QObject *>( data( index, LayoutRole ) ) ) )
return r;
else
return nullptr;
}
QModelIndex QgsLayoutManagerModel::indexFromLayout( QgsMasterLayoutInterface *layout ) const
{
if ( !mLayoutManager )
{
return QModelIndex();
}
const int r = mLayoutManager->layouts().indexOf( layout );
if ( r < 0 )
return QModelIndex();
QModelIndex idx = index( mAllowEmpty ? r + 1 : r, 0, QModelIndex() );
if ( idx.isValid() )
{
return idx;
}
return QModelIndex();
}
void QgsLayoutManagerModel::setAllowEmptyLayout( bool allowEmpty )
{
if ( allowEmpty == mAllowEmpty )
return;
if ( allowEmpty )
{
beginInsertRows( QModelIndex(), 0, 0 );
mAllowEmpty = true;
endInsertRows();
}
else
{
beginRemoveRows( QModelIndex(), 0, 0 );
mAllowEmpty = false;
endRemoveRows();
}
}
void QgsLayoutManagerModel::layoutAboutToBeAdded( const QString & )
{
int row = mLayoutManager->layouts().count() + ( mAllowEmpty ? 1 : 0 );
beginInsertRows( QModelIndex(), row, row );
}
void QgsLayoutManagerModel::layoutAboutToBeRemoved( const QString &name )
{
QgsMasterLayoutInterface *l = mLayoutManager->layoutByName( name );
int row = mLayoutManager->layouts().indexOf( l ) + ( mAllowEmpty ? 1 : 0 );
if ( row >= 0 )
beginRemoveRows( QModelIndex(), row, row );
}
void QgsLayoutManagerModel::layoutAdded( const QString & )
{
endInsertRows();
}
void QgsLayoutManagerModel::layoutRemoved( const QString & )
{
endRemoveRows();
}
void QgsLayoutManagerModel::layoutRenamed( QgsMasterLayoutInterface *layout, const QString & )
{
int row = mLayoutManager->layouts().indexOf( layout ) + ( mAllowEmpty ? 1 : 0 );
QModelIndex index = createIndex( row, 0 );
emit dataChanged( index, index, QVector<int>() << Qt::DisplayRole );
}
//
// QgsLayoutManagerProxyModel
//
QgsLayoutManagerProxyModel::QgsLayoutManagerProxyModel( QObject *parent )
: QSortFilterProxyModel( parent )
{
setDynamicSortFilter( true );
sort( 0 );
setSortCaseSensitivity( Qt::CaseInsensitive );
}
bool QgsLayoutManagerProxyModel::lessThan( const QModelIndex &left, const QModelIndex &right ) const
{
const QString leftText = sourceModel()->data( left, Qt::DisplayRole ).toString();
const QString rightText = sourceModel()->data( right, Qt::DisplayRole ).toString();
if ( leftText.isEmpty() )
return true;
if ( rightText.isEmpty() )
return false;
return QString::localeAwareCompare( leftText, rightText ) < 0;
}
bool QgsLayoutManagerProxyModel::filterAcceptsRow( int sourceRow, const QModelIndex &sourceParent ) const
{
QgsLayoutManagerModel *model = qobject_cast< QgsLayoutManagerModel * >( sourceModel() );
if ( !model )
return false;
QgsMasterLayoutInterface *layout = model->layoutFromIndex( model->index( sourceRow, 0, sourceParent ) );
if ( !layout )
return model->allowEmptyLayout();
switch ( layout->layoutType() )
{
case QgsMasterLayoutInterface::PrintLayout:
return mFilters & FilterPrintLayouts;
case QgsMasterLayoutInterface::Report:
return mFilters & FilterReports;
}
return false;
}
QgsLayoutManagerProxyModel::Filters QgsLayoutManagerProxyModel::filters() const
{
return mFilters;
}
void QgsLayoutManagerProxyModel::setFilters( Filters filters )
{
mFilters = filters;
invalidateFilter();
}

View File

@ -20,6 +20,8 @@
#include "qgis_sip.h"
#include "qgsmasterlayoutinterface.h"
#include <QObject>
#include <QAbstractListModel>
#include <QSortFilterProxyModel>
class QgsProject;
class QgsPrintLayout;
@ -38,7 +40,6 @@ class QgsPrintLayout;
* in the manager.
* \since QGIS 3.0
*/
class CORE_EXPORT QgsLayoutManager : public QObject
{
Q_OBJECT
@ -146,4 +147,123 @@ class CORE_EXPORT QgsLayoutManager : public QObject
};
/**
* \ingroup core
* \class QgsLayoutManagerModel
*
* List model representing the print layouts and reports available in a
* layout manager.
*
* \since QGIS 3.8
*/
class CORE_EXPORT QgsLayoutManagerModel : public QAbstractListModel
{
Q_OBJECT
public:
//! Custom model roles
enum Role
{
LayoutRole = Qt::UserRole + 1, //!< Layout object
};
/**
* Constructor for QgsLayoutManagerModel, showing the layouts from the specified \a manager.
*/
explicit QgsLayoutManagerModel( QgsLayoutManager *manager, QObject *parent SIP_TRANSFERTHIS = nullptr );
int rowCount( const QModelIndex &parent ) const override;
QVariant data( const QModelIndex &index, int role ) const override;
bool setData( const QModelIndex &index, const QVariant &value, int role = Qt::EditRole ) override;
Qt::ItemFlags flags( const QModelIndex &index ) const override;
/**
* Returns the layout at the corresponding \a index.
* \see indexFromLayout()
*/
QgsMasterLayoutInterface *layoutFromIndex( const QModelIndex &index ) const;
/**
* Returns the model index corresponding to a \a layout.
* \see layoutFromIndex()
*/
QModelIndex indexFromLayout( QgsMasterLayoutInterface *layout ) const;
/**
* Sets whether an optional empty layout ("not set") option is present in the model.
* \see allowEmptyLayout()
*/
void setAllowEmptyLayout( bool allowEmpty );
/**
* Returns TRUE if the model allows the empty layout ("not set") choice.
* \see setAllowEmptyLayout()
*/
bool allowEmptyLayout() const { return mAllowEmpty; }
private slots:
void layoutAboutToBeAdded( const QString &name );
void layoutAboutToBeRemoved( const QString &name );
void layoutAdded( const QString &name );
void layoutRemoved( const QString &name );
void layoutRenamed( QgsMasterLayoutInterface *layout, const QString &newName );
private:
QgsLayoutManager *mLayoutManager = nullptr;
bool mAllowEmpty = false;
};
/**
* \ingroup core
* \class QgsLayoutManagerProxyModel
*
* QSortFilterProxyModel subclass for QgsLayoutManagerModel
*
* \since QGIS 3.8
*/
class CORE_EXPORT QgsLayoutManagerProxyModel : public QSortFilterProxyModel
{
Q_OBJECT
public:
//! Available filter flags for filtering the model
enum Filter
{
FilterPrintLayouts = 1 << 1, //!< Includes print layouts
FilterReports = 1 << 2, //!< Includes reports
};
Q_DECLARE_FLAGS( Filters, Filter )
Q_FLAG( Filters )
/**
* Constructor for QgsLayoutManagerProxyModel.
*/
explicit QgsLayoutManagerProxyModel( QObject *parent SIP_TRANSFERTHIS = nullptr );
bool lessThan( const QModelIndex &left, const QModelIndex &right ) const override;
bool filterAcceptsRow( int sourceRow, const QModelIndex &sourceParent ) const override;
/**
* Returns the current filters used for filtering available layouts.
*
* \see setFilters()
*/
QgsLayoutManagerProxyModel::Filters filters() const;
/**
* Sets the current \a filters used for filtering available layouts.
*
* \see filters()
*/
void setFilters( QgsLayoutManagerProxyModel::Filters filters );
private:
Filters mFilters = Filters( FilterPrintLayouts | FilterReports );
};
Q_DECLARE_OPERATORS_FOR_FLAGS( QgsLayoutManagerProxyModel::Filters )
#endif // QGSLAYOUTMANAGER_H

View File

@ -90,6 +90,7 @@ ADD_PYTHON_TEST(PyQgsLayoutAtlas test_qgslayoutatlas.py)
ADD_PYTHON_TEST(PyQgsLayoutExporter test_qgslayoutexporter.py)
ADD_PYTHON_TEST(PyQgsLayoutFrame test_qgslayoutframe.py)
ADD_PYTHON_TEST(PyQgsLayoutManager test_qgslayoutmanager.py)
ADD_PYTHON_TEST(PyQgsLayoutManagerModel test_qgslayoutmanagermodel.py)
ADD_PYTHON_TEST(PyQgsLayoutPageCollection test_qgslayoutpagecollection.py)
ADD_PYTHON_TEST(PyQgsLayoutView test_qgslayoutview.py)
ADD_PYTHON_TEST(PyQgsLayoutGridSettings test_qgslayoutgridsettings.py)

View File

@ -0,0 +1,228 @@
# -*- coding: utf-8 -*-
"""QGIS Unit tests for QgsLayoutManagerModel.
.. note:: This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
"""
__author__ = '(C) 2019 by Nyall Dawson'
__date__ = '11/03/2019'
__copyright__ = 'Copyright 2019, The QGIS Project'
# This will get replaced with a git SHA1 when you do a git archive
__revision__ = '$Format:%H$'
import qgis # NOQA
from qgis.PyQt.QtXml import QDomDocument
from qgis.core import (QgsPrintLayout,
QgsLayoutManager,
QgsLayoutManagerModel,
QgsLayoutManagerProxyModel,
QgsProject,
QgsReport,
QgsMasterLayoutInterface)
from qgis.PyQt.QtCore import Qt, QModelIndex
from qgis.testing import start_app, unittest
from utilities import unitTestDataPath
from qgis.PyQt.QtXml import QDomDocument
from qgis.PyQt.QtTest import QSignalSpy
start_app()
TEST_DATA_DIR = unitTestDataPath()
class TestQgsLayoutManagerModel(unittest.TestCase):
def setUp(self):
"""Run before each test."""
self.manager = None
self.aboutFired = False
def tearDown(self):
"""Run after each test."""
pass
def testModel(self):
project = QgsProject()
manager = QgsLayoutManager(project)
model = QgsLayoutManagerModel(manager)
self.assertEqual(model.rowCount(QModelIndex()), 0)
self.assertEqual(model.data(model.index(0, 0, QModelIndex()), Qt.DisplayRole), None)
self.assertEqual(model.data(model.index(0, 0, QModelIndex()), QgsLayoutManagerModel.LayoutRole), None)
self.assertEqual(model.layoutFromIndex(model.index(0, 0, QModelIndex())), None)
self.assertEqual(model.indexFromLayout(None), QModelIndex())
layout = QgsPrintLayout(project)
layout.setName('test layout')
self.assertEqual(model.indexFromLayout(layout), QModelIndex())
self.assertTrue(manager.addLayout(layout))
self.assertEqual(model.rowCount(QModelIndex()), 1)
self.assertEqual(model.data(model.index(0, 0, QModelIndex()), Qt.DisplayRole), 'test layout')
self.assertEqual(model.data(model.index(0, 0, QModelIndex()), QgsLayoutManagerModel.LayoutRole), layout)
self.assertEqual(model.layoutFromIndex(model.index(0, 0, QModelIndex())), layout)
self.assertEqual(model.indexFromLayout(layout), model.index(0, 0, QModelIndex()))
self.assertEqual(model.data(model.index(1, 0, QModelIndex()), Qt.DisplayRole), None)
self.assertEqual(model.data(model.index(1, 0, QModelIndex()), QgsLayoutManagerModel.LayoutRole), None)
self.assertEqual(model.layoutFromIndex(model.index(1, 0, QModelIndex())), None)
layout.setName('test Layout')
self.assertEqual(model.data(model.index(0, 0, QModelIndex()), Qt.DisplayRole), 'test Layout')
layout2 = QgsPrintLayout(project)
layout2.setName('test layout2')
self.assertTrue(manager.addLayout(layout2))
self.assertEqual(model.rowCount(QModelIndex()), 2)
self.assertEqual(model.data(model.index(0, 0, QModelIndex()), Qt.DisplayRole), 'test Layout')
self.assertEqual(model.data(model.index(0, 0, QModelIndex()), QgsLayoutManagerModel.LayoutRole), layout)
self.assertEqual(model.layoutFromIndex(model.index(0, 0, QModelIndex())), layout)
self.assertEqual(model.indexFromLayout(layout), model.index(0, 0, QModelIndex()))
self.assertEqual(model.data(model.index(1, 0, QModelIndex()), Qt.DisplayRole), 'test layout2')
self.assertEqual(model.data(model.index(1, 0, QModelIndex()), QgsLayoutManagerModel.LayoutRole), layout2)
self.assertEqual(model.layoutFromIndex(model.index(1, 0, QModelIndex())), layout2)
self.assertEqual(model.indexFromLayout(layout2), model.index(1, 0, QModelIndex()))
manager.removeLayout(layout)
self.assertEqual(model.rowCount(QModelIndex()), 1)
self.assertEqual(model.data(model.index(0, 0, QModelIndex()), Qt.DisplayRole), 'test layout2')
self.assertEqual(model.data(model.index(0, 0, QModelIndex()), QgsLayoutManagerModel.LayoutRole), layout2)
self.assertEqual(model.data(model.index(1, 0, QModelIndex()), Qt.DisplayRole), None)
self.assertEqual(model.data(model.index(1, 0, QModelIndex()), QgsLayoutManagerModel.LayoutRole), None)
self.assertEqual(model.layoutFromIndex(model.index(0, 0, QModelIndex())), layout2)
self.assertEqual(model.layoutFromIndex(model.index(1, 0, QModelIndex())), None)
self.assertEqual(model.indexFromLayout(layout2), model.index(0, 0, QModelIndex()))
manager.clear()
self.assertEqual(model.rowCount(QModelIndex()), 0)
self.assertEqual(model.data(model.index(0, 0, QModelIndex()), Qt.DisplayRole), None)
self.assertEqual(model.data(model.index(0, 0, QModelIndex()), QgsLayoutManagerModel.LayoutRole), None)
self.assertEqual(model.layoutFromIndex(model.index(0, 0, QModelIndex())), None)
# with empty row
model.setAllowEmptyLayout(True)
self.assertEqual(model.rowCount(QModelIndex()), 1)
self.assertEqual(model.data(model.index(0, 0, QModelIndex()), Qt.DisplayRole), None)
self.assertEqual(model.data(model.index(0, 0, QModelIndex()), QgsLayoutManagerModel.LayoutRole), None)
self.assertEqual(model.layoutFromIndex(model.index(0, 0, QModelIndex())), None)
layout = QgsPrintLayout(project)
layout.setName('test layout')
self.assertTrue(manager.addLayout(layout))
self.assertEqual(model.rowCount(QModelIndex()), 2)
self.assertEqual(model.data(model.index(0, 0, QModelIndex()), Qt.DisplayRole), None)
self.assertEqual(model.data(model.index(0, 0, QModelIndex()), QgsLayoutManagerModel.LayoutRole), None)
self.assertEqual(model.data(model.index(1, 0, QModelIndex()), Qt.DisplayRole), 'test layout')
self.assertEqual(model.data(model.index(1, 0, QModelIndex()), QgsLayoutManagerModel.LayoutRole), layout)
self.assertEqual(model.data(model.index(2, 0, QModelIndex()), Qt.DisplayRole), None)
self.assertEqual(model.data(model.index(2, 0, QModelIndex()), QgsLayoutManagerModel.LayoutRole), None)
self.assertEqual(model.layoutFromIndex(model.index(0, 0, QModelIndex())), None)
self.assertEqual(model.layoutFromIndex(model.index(1, 0, QModelIndex())), layout)
self.assertEqual(model.indexFromLayout(layout), model.index(1, 0, QModelIndex()))
layout2 = QgsPrintLayout(project)
layout2.setName('test layout2')
self.assertTrue(manager.addLayout(layout2))
self.assertEqual(model.rowCount(QModelIndex()), 3)
self.assertEqual(model.data(model.index(0, 0, QModelIndex()), Qt.DisplayRole), None)
self.assertEqual(model.data(model.index(0, 0, QModelIndex()), QgsLayoutManagerModel.LayoutRole), None)
self.assertEqual(model.data(model.index(1, 0, QModelIndex()), Qt.DisplayRole), 'test layout')
self.assertEqual(model.data(model.index(1, 0, QModelIndex()), QgsLayoutManagerModel.LayoutRole), layout)
self.assertEqual(model.data(model.index(2, 0, QModelIndex()), Qt.DisplayRole), 'test layout2')
self.assertEqual(model.data(model.index(2, 0, QModelIndex()), QgsLayoutManagerModel.LayoutRole), layout2)
self.assertEqual(model.layoutFromIndex(model.index(0, 0, QModelIndex())), None)
self.assertEqual(model.layoutFromIndex(model.index(1, 0, QModelIndex())), layout)
self.assertEqual(model.layoutFromIndex(model.index(2, 0, QModelIndex())), layout2)
self.assertEqual(model.indexFromLayout(layout), model.index(1, 0, QModelIndex()))
self.assertEqual(model.indexFromLayout(layout2), model.index(2, 0, QModelIndex()))
manager.clear()
self.assertEqual(model.rowCount(QModelIndex()), 1)
self.assertEqual(model.data(model.index(0, 0, QModelIndex()), Qt.DisplayRole), None)
self.assertEqual(model.data(model.index(0, 0, QModelIndex()), QgsLayoutManagerModel.LayoutRole), None)
self.assertEqual(model.layoutFromIndex(model.index(0, 0, QModelIndex())), None)
def testProxyModel(self):
project = QgsProject()
manager = QgsLayoutManager(project)
model = QgsLayoutManagerModel(manager)
proxy = QgsLayoutManagerProxyModel()
proxy.setSourceModel(model)
self.assertEqual(proxy.rowCount(QModelIndex()), 0)
self.assertEqual(proxy.data(model.index(0, 0, QModelIndex()), Qt.DisplayRole), None)
self.assertEqual(proxy.data(model.index(0, 0, QModelIndex()), QgsLayoutManagerModel.LayoutRole), None)
layout = QgsPrintLayout(project)
layout.setName('ccc')
self.assertTrue(manager.addLayout(layout))
self.assertEqual(proxy.rowCount(QModelIndex()), 1)
self.assertEqual(proxy.data(proxy.index(0, 0, QModelIndex()), Qt.DisplayRole), 'ccc')
self.assertEqual(proxy.data(proxy.index(0, 0, QModelIndex()), QgsLayoutManagerModel.LayoutRole), layout)
self.assertEqual(proxy.data(proxy.index(1, 0, QModelIndex()), Qt.DisplayRole), None)
self.assertEqual(proxy.data(proxy.index(1, 0, QModelIndex()), QgsLayoutManagerModel.LayoutRole), None)
layout2 = QgsPrintLayout(project)
layout2.setName('bbb')
self.assertTrue(manager.addLayout(layout2))
self.assertEqual(proxy.rowCount(QModelIndex()), 2)
self.assertEqual(proxy.data(proxy.index(0, 0, QModelIndex()), Qt.DisplayRole), 'bbb')
self.assertEqual(proxy.data(proxy.index(0, 0, QModelIndex()), QgsLayoutManagerModel.LayoutRole), layout2)
self.assertEqual(proxy.data(proxy.index(1, 0, QModelIndex()), Qt.DisplayRole), 'ccc')
self.assertEqual(proxy.data(proxy.index(1, 0, QModelIndex()), QgsLayoutManagerModel.LayoutRole), layout)
layout.setName('aaa')
self.assertEqual(proxy.data(proxy.index(0, 0, QModelIndex()), Qt.DisplayRole), 'aaa')
self.assertEqual(proxy.data(proxy.index(0, 0, QModelIndex()), QgsLayoutManagerModel.LayoutRole), layout)
self.assertEqual(proxy.data(proxy.index(1, 0, QModelIndex()), Qt.DisplayRole), 'bbb')
self.assertEqual(proxy.data(proxy.index(1, 0, QModelIndex()), QgsLayoutManagerModel.LayoutRole), layout2)
model.setAllowEmptyLayout(True)
self.assertEqual(proxy.rowCount(QModelIndex()), 3)
self.assertEqual(proxy.data(proxy.index(0, 0, QModelIndex()), Qt.DisplayRole), None)
self.assertEqual(proxy.data(proxy.index(0, 0, QModelIndex()), QgsLayoutManagerModel.LayoutRole), None)
self.assertEqual(proxy.data(proxy.index(1, 0, QModelIndex()), Qt.DisplayRole), 'aaa')
self.assertEqual(proxy.data(proxy.index(1, 0, QModelIndex()), QgsLayoutManagerModel.LayoutRole), layout)
self.assertEqual(proxy.data(proxy.index(2, 0, QModelIndex()), Qt.DisplayRole), 'bbb')
self.assertEqual(proxy.data(proxy.index(2, 0, QModelIndex()), QgsLayoutManagerModel.LayoutRole), layout2)
r = QgsReport(project)
r.setName('ddd')
manager.addLayout(r)
self.assertEqual(proxy.rowCount(QModelIndex()), 4)
self.assertEqual(proxy.data(proxy.index(0, 0, QModelIndex()), Qt.DisplayRole), None)
self.assertEqual(proxy.data(proxy.index(0, 0, QModelIndex()), QgsLayoutManagerModel.LayoutRole), None)
self.assertEqual(proxy.data(proxy.index(1, 0, QModelIndex()), Qt.DisplayRole), 'aaa')
self.assertEqual(proxy.data(proxy.index(1, 0, QModelIndex()), QgsLayoutManagerModel.LayoutRole), layout)
self.assertEqual(proxy.data(proxy.index(2, 0, QModelIndex()), Qt.DisplayRole), 'bbb')
self.assertEqual(proxy.data(proxy.index(2, 0, QModelIndex()), QgsLayoutManagerModel.LayoutRole), layout2)
self.assertEqual(proxy.data(proxy.index(3, 0, QModelIndex()), Qt.DisplayRole), 'ddd')
self.assertEqual(proxy.data(proxy.index(3, 0, QModelIndex()), QgsLayoutManagerModel.LayoutRole), r)
proxy.setFilters(QgsLayoutManagerProxyModel.FilterPrintLayouts)
self.assertEqual(proxy.filters(), QgsLayoutManagerProxyModel.FilterPrintLayouts)
self.assertEqual(proxy.rowCount(QModelIndex()), 3)
self.assertEqual(proxy.data(proxy.index(0, 0, QModelIndex()), Qt.DisplayRole), None)
self.assertEqual(proxy.data(proxy.index(0, 0, QModelIndex()), QgsLayoutManagerModel.LayoutRole), None)
self.assertEqual(proxy.data(proxy.index(1, 0, QModelIndex()), Qt.DisplayRole), 'aaa')
self.assertEqual(proxy.data(proxy.index(1, 0, QModelIndex()), QgsLayoutManagerModel.LayoutRole), layout)
self.assertEqual(proxy.data(proxy.index(2, 0, QModelIndex()), Qt.DisplayRole), 'bbb')
self.assertEqual(proxy.data(proxy.index(2, 0, QModelIndex()), QgsLayoutManagerModel.LayoutRole), layout2)
proxy.setFilters(QgsLayoutManagerProxyModel.FilterReports)
self.assertEqual(proxy.filters(), QgsLayoutManagerProxyModel.FilterReports)
self.assertEqual(proxy.rowCount(QModelIndex()), 2)
self.assertEqual(proxy.data(proxy.index(0, 0, QModelIndex()), Qt.DisplayRole), None)
self.assertEqual(proxy.data(proxy.index(0, 0, QModelIndex()), QgsLayoutManagerModel.LayoutRole), None)
self.assertEqual(proxy.data(proxy.index(1, 0, QModelIndex()), Qt.DisplayRole), 'ddd')
self.assertEqual(proxy.data(proxy.index(1, 0, QModelIndex()), QgsLayoutManagerModel.LayoutRole), r)
proxy.setFilters(QgsLayoutManagerProxyModel.FilterPrintLayouts | QgsLayoutManagerProxyModel.FilterReports)
self.assertEqual(proxy.filters(), QgsLayoutManagerProxyModel.FilterPrintLayouts | QgsLayoutManagerProxyModel.FilterReports)
self.assertEqual(proxy.rowCount(QModelIndex()), 4)
if __name__ == '__main__':
unittest.main()