Skip to content
Permalink
Browse files

Tweaks to layout item combo box

  • Loading branch information
nyalldawson committed Mar 11, 2019
1 parent 0003e91 commit f9fb4085b2f573ffac0deaca3953cd5c934253d3
@@ -155,6 +155,38 @@ Returns the QgsLayoutItem corresponding to an index from the source
QgsLayoutModel model.
%End

QgsLayout *layout();
%Docstring
Returns the associated layout.

.. versionadded:: 3.8
%End

void setAllowEmptyItem( bool allowEmpty );
%Docstring
Sets whether an optional empty layout item is present in the model.

.. seealso:: :py:func:`allowEmptyItem`

.. versionadded:: 3.8
%End

bool allowEmptyItem() const;
%Docstring
Returns ``True`` if the model includes the empty item choice.

.. seealso:: :py:func:`setAllowEmptyItem`

.. versionadded:: 3.8
%End

virtual int rowCount( const QModelIndex &parent = QModelIndex() ) const;

virtual QVariant data( const QModelIndex &index, int role = Qt::DisplayRole ) const;

virtual bool setData( const QModelIndex &index, const QVariant &value, int role = Qt::EditRole );


protected:
virtual bool filterAcceptsRow( int sourceRow, const QModelIndex &sourceParent ) const;

@@ -33,6 +33,15 @@ until setCurrentLayout() is called
void setCurrentLayout( QgsLayout *layout );
%Docstring
Sets the ``layout`` containing the items to list in the combo box.

.. seealso:: :py:func:`currentLayout`
%End

QgsLayout *currentLayout();
%Docstring
Returns the current layout containing the items shown in the combo box.

.. seealso:: :py:func:`setCurrentLayout`
%End

void setItemType( QgsLayoutItemRegistry::ItemType itemType );
@@ -64,6 +73,24 @@ Sets a list of specific items to exclude from the combo box.
Returns the list of specific items excluded from the combo box.

.. seealso:: :py:func:`setExceptedItemList`
%End

void setAllowEmptyItem( bool allowEmpty );
%Docstring
Sets whether an optional empty layout item is present in the combobox.

.. seealso:: :py:func:`allowEmptyItem`

.. versionadded:: 3.8
%End

bool allowEmptyItem() const;
%Docstring
Returns ``True`` if the model includes the empty item choice.

.. seealso:: :py:func:`setAllowEmptyItem`

.. versionadded:: 3.8
%End

QgsLayoutItem *item( int index ) const;
@@ -323,7 +323,7 @@ QgsLayoutManagerModel::QgsLayoutManagerModel( QgsLayoutManager *manager, QObject
int QgsLayoutManagerModel::rowCount( const QModelIndex &parent ) const
{
Q_UNUSED( parent );
return mLayoutManager->layouts().count() + ( mAllowEmpty ? 1 : 0 );
return ( mLayoutManager ? mLayoutManager->layouts().count() : 0 ) + ( mAllowEmpty ? 1 : 0 );
}

QVariant QgsLayoutManagerModel::data( const QModelIndex &index, int role ) const
@@ -339,11 +339,11 @@ QVariant QgsLayoutManagerModel::data( const QModelIndex &index, int role ) const
case Qt::DisplayRole:
case Qt::ToolTipRole:
case Qt::EditRole:
return !isEmpty ? mLayoutManager->layouts().at( layoutRow )->name() : QVariant();
return !isEmpty && mLayoutManager ? mLayoutManager->layouts().at( layoutRow )->name() : QVariant();

case LayoutRole:
{
if ( isEmpty )
if ( isEmpty || !mLayoutManager )
return QVariant();
else if ( QgsLayout *l = dynamic_cast< QgsLayout * >( mLayoutManager->layouts().at( layoutRow ) ) )
return QVariant::fromValue( l );
@@ -355,7 +355,7 @@ QVariant QgsLayoutManagerModel::data( const QModelIndex &index, int role ) const

case Qt::DecorationRole:
{
return isEmpty ? QIcon() : mLayoutManager->layouts().at( layoutRow )->icon();
return isEmpty || !mLayoutManager ? QIcon() : mLayoutManager->layouts().at( layoutRow )->icon();
}

default:
@@ -958,6 +958,13 @@ QgsLayoutProxyModel::QgsLayoutProxyModel( QgsLayout *layout, QObject *parent )

bool QgsLayoutProxyModel::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;

//sort by item id
const QgsLayoutItem *item1 = itemFromSourceIndex( left );
const QgsLayoutItem *item2 = itemFromSourceIndex( right );
@@ -970,6 +977,35 @@ bool QgsLayoutProxyModel::lessThan( const QModelIndex &left, const QModelIndex &
return QString::localeAwareCompare( item1->displayName(), item2->displayName() ) < 0;
}

int QgsLayoutProxyModel::rowCount( const QModelIndex &parent ) const
{
return QSortFilterProxyModel::rowCount( parent ) + ( mAllowEmpty ? 1 : 0 );
}

QVariant QgsLayoutProxyModel::data( const QModelIndex &index, int role ) const
{
if ( mAllowEmpty && index.row() == rowCount() - 1 )
{
return QVariant();
}
else
{
return QSortFilterProxyModel::data( index, role );
}
}

bool QgsLayoutProxyModel::setData( const QModelIndex &index, const QVariant &value, int role )
{
if ( mAllowEmpty && index.row() == rowCount() - 1 )
{
return false;
}
else
{
return QSortFilterProxyModel::setData( index, value, role );
}
}

QgsLayoutItem *QgsLayoutProxyModel::itemFromSourceIndex( const QModelIndex &sourceIndex ) const
{
if ( !mLayout )
@@ -980,6 +1016,17 @@ QgsLayoutItem *QgsLayoutProxyModel::itemFromSourceIndex( const QModelIndex &sour
return qobject_cast<QgsLayoutItem *>( itemAsVariant.value<QObject *>() );
}

void QgsLayoutProxyModel::setAllowEmptyItem( bool allowEmpty )
{
mAllowEmpty = allowEmpty;
invalidateFilter();
}

bool QgsLayoutProxyModel::allowEmptyItem() const
{
return mAllowEmpty;
}

void QgsLayoutProxyModel::setFilterType( QgsLayoutItemRegistry::ItemType filter )
{
mItemTypeFilter = filter;
@@ -1002,7 +1049,7 @@ bool QgsLayoutProxyModel::filterAcceptsRow( int sourceRow, const QModelIndex &so
QgsLayoutItem *item = itemFromSourceIndex( index );

if ( !item )
return false;
return mAllowEmpty;

// specific exceptions
if ( mExceptedList.contains( item ) )
@@ -352,6 +352,30 @@ class CORE_EXPORT QgsLayoutProxyModel: public QSortFilterProxyModel
*/
QgsLayoutItem *itemFromSourceIndex( const QModelIndex &sourceIndex ) const;

/**
* Returns the associated layout.
* \since QGIS 3.8
*/
QgsLayout *layout() { return mLayout; }

/**
* Sets whether an optional empty layout item is present in the model.
* \see allowEmptyItem()
* \since QGIS 3.8
*/
void setAllowEmptyItem( bool allowEmpty );

/**
* Returns TRUE if the model includes the empty item choice.
* \see setAllowEmptyItem()
* \since QGIS 3.8
*/
bool allowEmptyItem() const;

int rowCount( const QModelIndex &parent = QModelIndex() ) const override;
QVariant data( const QModelIndex &index, int role = Qt::DisplayRole ) const override;
bool setData( const QModelIndex &index, const QVariant &value, int role = Qt::EditRole ) override;

protected:
bool filterAcceptsRow( int sourceRow, const QModelIndex &sourceParent ) const override;
bool lessThan( const QModelIndex &left, const QModelIndex &right ) const override;
@@ -360,6 +384,7 @@ class CORE_EXPORT QgsLayoutProxyModel: public QSortFilterProxyModel
QgsLayout *mLayout = nullptr;
QgsLayoutItemRegistry::ItemType mItemTypeFilter;
QList< QgsLayoutItem * > mExceptedList;
bool mAllowEmpty = false;

};

@@ -35,6 +35,11 @@ void QgsLayoutItemComboBox::setCurrentLayout( QgsLayout *layout )
mProxyModel->sort( 0, Qt::AscendingOrder );
}

QgsLayout *QgsLayoutItemComboBox::currentLayout()
{
return mProxyModel->layout();
}

void QgsLayoutItemComboBox::setItem( const QgsLayoutItem *item )
{
if ( !mProxyModel->sourceLayerModel() )
@@ -50,7 +55,7 @@ void QgsLayoutItemComboBox::setItem( const QgsLayoutItem *item )
return;
}
}
setCurrentIndex( -1 );
setCurrentIndex( mProxyModel->allowEmptyItem() ? 0 : -1 );
}

QgsLayoutItem *QgsLayoutItemComboBox::currentItem() const
@@ -97,6 +102,16 @@ QList< QgsLayoutItem *> QgsLayoutItemComboBox::exceptedItemList() const
return mProxyModel->exceptedItemList();
}

void QgsLayoutItemComboBox::setAllowEmptyItem( bool allowEmpty )
{
mProxyModel->setAllowEmptyItem( allowEmpty );
}

bool QgsLayoutItemComboBox::allowEmptyItem() const
{
return mProxyModel->allowEmptyItem();
}

QgsLayoutItem *QgsLayoutItemComboBox::item( int index ) const
{
const QModelIndex proxyIndex = mProxyModel->index( index, 0 );
@@ -46,9 +46,18 @@ class GUI_EXPORT QgsLayoutItemComboBox : public QComboBox

/**
* Sets the \a layout containing the items to list in the combo box.
*
* \see currentLayout()
*/
void setCurrentLayout( QgsLayout *layout );

/**
* Returns the current layout containing the items shown in the combo box.
*
* \see setCurrentLayout()
*/
QgsLayout *currentLayout();

/**
* Sets a filter for the item type to show in the combo box.
* \param itemType type of items to show. Set to QgsLayoutItemRegistry::LayoutItem to
@@ -75,6 +84,20 @@ class GUI_EXPORT QgsLayoutItemComboBox : public QComboBox
*/
QList< QgsLayoutItem * > exceptedItemList() const;

/**
* Sets whether an optional empty layout item is present in the combobox.
* \see allowEmptyItem()
* \since QGIS 3.8
*/
void setAllowEmptyItem( bool allowEmpty );

/**
* Returns TRUE if the model includes the empty item choice.
* \see setAllowEmptyItem()
* \since QGIS 3.8
*/
bool allowEmptyItem() const;

/**
* Returns the item currently shown at the specified \a index within the combo box.
* \see currentItem()
@@ -21,6 +21,7 @@
#include "qgsapplication.h"
#include "qgsmapsettings.h"
#include "qgsproject.h"
#include "qgslayoutitemlabel.h"

#include <QObject>
#include "qgstest.h"
@@ -58,6 +59,7 @@ class TestQgsLayoutModel : public QObject
void reorderToTopWithRemoved(); //test reordering to top with removed items
void reorderToBottomWithRemoved(); //test reordering to bottom with removed items

void proxy();
void proxyCrash();

};
@@ -771,6 +773,48 @@ void TestQgsLayoutModel::reorderToBottomWithRemoved()
QCOMPARE( layout.itemsModel()->mItemsInScene.at( 1 ), item2 );
}

void TestQgsLayoutModel::proxy()
{
QgsLayout *layout = new QgsLayout( QgsProject::instance() );
QgsLayoutProxyModel *proxy = new QgsLayoutProxyModel( layout );
// add some items to composition
QgsLayoutItemMap *item1 = new QgsLayoutItemMap( layout );
item1->setId( QStringLiteral( "c" ) );
layout->addLayoutItem( item1 );
QgsLayoutItemMap *item2 = new QgsLayoutItemMap( layout );
item2->setId( QStringLiteral( "b" ) );
layout->addLayoutItem( item2 );
QgsLayoutItemLabel *item3 = new QgsLayoutItemLabel( layout );
item3->setId( QStringLiteral( "a" ) );
layout->addLayoutItem( item3 );
QCOMPARE( proxy->rowCount( QModelIndex() ), 3 );
QCOMPARE( proxy->data( proxy->index( 0, 2, QModelIndex() ) ).toString(), QStringLiteral( "a" ) );
QCOMPARE( proxy->data( proxy->index( 1, 2, QModelIndex() ) ).toString(), QStringLiteral( "b" ) );
QCOMPARE( proxy->data( proxy->index( 2, 2, QModelIndex() ) ).toString(), QStringLiteral( "c" ) );

proxy->setAllowEmptyItem( true );
QCOMPARE( proxy->rowCount( QModelIndex() ), 4 );
QCOMPARE( proxy->data( proxy->index( 0, 2, QModelIndex() ) ).toString(), QStringLiteral( "a" ) );
QCOMPARE( proxy->data( proxy->index( 1, 2, QModelIndex() ) ).toString(), QStringLiteral( "b" ) );
QCOMPARE( proxy->data( proxy->index( 2, 2, QModelIndex() ) ).toString(), QStringLiteral( "c" ) );
QCOMPARE( proxy->data( proxy->index( 3, 2, QModelIndex() ) ).toString(), QString() );

proxy->setFilterType( QgsLayoutItemRegistry::LayoutMap );
QCOMPARE( proxy->rowCount( QModelIndex() ), 3 );
QCOMPARE( proxy->data( proxy->index( 0, 2, QModelIndex() ) ).toString(), QStringLiteral( "b" ) );
QCOMPARE( proxy->data( proxy->index( 1, 2, QModelIndex() ) ).toString(), QStringLiteral( "c" ) );
QCOMPARE( proxy->data( proxy->index( 2, 2, QModelIndex() ) ).toString(), QString() );

proxy->setFilterType( QgsLayoutItemRegistry::LayoutLabel );
QCOMPARE( proxy->rowCount( QModelIndex() ), 2 );
QCOMPARE( proxy->data( proxy->index( 0, 2, QModelIndex() ) ).toString(), QStringLiteral( "a" ) );
QCOMPARE( proxy->data( proxy->index( 1, 2, QModelIndex() ) ).toString(), QString() );

proxy->setFilterType( QgsLayoutItemRegistry::LayoutScaleBar );
QCOMPARE( proxy->rowCount( QModelIndex() ), 1 );
QCOMPARE( proxy->data( proxy->index( 0, 2, QModelIndex() ) ).toString(), QString() );
}

void TestQgsLayoutModel::proxyCrash()
{
// test for a possible crash when using QgsComposerProxyModel and reordering items

0 comments on commit f9fb408

Please sign in to comment.
You can’t perform that action at this time.