Skip to content

Commit

Permalink
Allow not set choice in QgsMapLayerComboBox
Browse files Browse the repository at this point in the history
  • Loading branch information
nyalldawson committed Nov 16, 2016
1 parent 6638569 commit 959f97f
Show file tree
Hide file tree
Showing 9 changed files with 424 additions and 10 deletions.
16 changes: 16 additions & 0 deletions python/core/qgsmaplayermodel.sip
Expand Up @@ -19,6 +19,7 @@ class QgsMapLayerModel : QAbstractItemModel
{
LayerIdRole, /*!< Stores the map layer ID */
LayerRole, /*!< Stores pointer to the map layer itself */
IsEmptyRole, //!< True if index corresponds to the empty (not set) value
};

/**
Expand All @@ -38,6 +39,21 @@ class QgsMapLayerModel : QAbstractItemModel
* @brief checkAll changes the checkstate for all the layers
*/
void checkAll( Qt::CheckState checkState );

/**
* Sets whether an optional empty layer ("not set") option is present in the model.
* @see allowEmptyLayer()
* @note added in QGIS 3.0
*/
void setAllowEmptyLayer( bool allowEmpty );

/**
* Returns true if the model allows the empty layer ("not set") choice.
* @see setAllowEmptyLayer()
* @note added in QGIS 3.0
*/
bool allowEmptyLayer() const;

/**
* @brief layersChecked returns the list of layers which are checked (or unchecked)
*/
Expand Down
14 changes: 14 additions & 0 deletions python/gui/qgsmaplayercombobox.sip
Expand Up @@ -28,6 +28,20 @@ class QgsMapLayerComboBox : QComboBox
//! returns the list of excepted layers
QList<QgsMapLayer*> exceptedLayerList() const;

/**
* Sets whether an optional empty layer ("not set") option is shown in the combo box.
* @see allowEmptyLayer()
* @note added in QGIS 3.0
*/
void setAllowEmptyLayer( bool allowEmpty );

/**
* Returns true if the combo box allows the empty layer ("not set") choice.
* @see setAllowEmptyLayer()
* @note added in QGIS 3.0
*/
bool allowEmptyLayer() const;

/** Returns the current layer selected in the combo box.
* @see layer
*/
Expand Down
83 changes: 74 additions & 9 deletions src/core/qgsmaplayermodel.cpp
Expand Up @@ -26,6 +26,7 @@ QgsMapLayerModel::QgsMapLayerModel( const QList<QgsMapLayer *>& layers, QObject
: QAbstractItemModel( parent )
, mLayersChecked( QMap<QString, Qt::CheckState>() )
, mItemCheckable( false )
, mAllowEmpty( false )
{
connect( QgsMapLayerRegistry::instance(), SIGNAL( layersWillBeRemoved( QStringList ) ), this, SLOT( removeLayers( QStringList ) ) );
addLayers( layers );
Expand All @@ -35,6 +36,7 @@ QgsMapLayerModel::QgsMapLayerModel( QObject *parent )
: QAbstractItemModel( parent )
, mLayersChecked( QMap<QString, Qt::CheckState>() )
, mItemCheckable( false )
, mAllowEmpty( false )
{
connect( QgsMapLayerRegistry::instance(), SIGNAL( layersAdded( QList<QgsMapLayer*> ) ), this, SLOT( addLayers( QList<QgsMapLayer*> ) ) );
connect( QgsMapLayerRegistry::instance(), SIGNAL( layersWillBeRemoved( QStringList ) ), this, SLOT( removeLayers( QStringList ) ) );
Expand All @@ -55,6 +57,25 @@ void QgsMapLayerModel::checkAll( Qt::CheckState checkState )
emit dataChanged( index( 0, 0 ), index( mLayers.length() - 1, 0 ) );
}

void QgsMapLayerModel::setAllowEmptyLayer( bool allowEmpty )
{
if ( allowEmpty == mAllowEmpty )
return;

if ( allowEmpty )
{
beginInsertRows( QModelIndex(), 0, 0 );
mAllowEmpty = true;
endInsertRows();
}
else
{
beginRemoveRows( QModelIndex(), 0, 0 );
mAllowEmpty = false;
endRemoveRows();
}
}

QList<QgsMapLayer *> QgsMapLayerModel::layersChecked( Qt::CheckState checkState )
{
QList<QgsMapLayer *> layers;
Expand All @@ -71,6 +92,8 @@ QList<QgsMapLayer *> QgsMapLayerModel::layersChecked( Qt::CheckState checkState
QModelIndex QgsMapLayerModel::indexFromLayer( QgsMapLayer *layer ) const
{
int r = mLayers.indexOf( layer );
if ( r >= 0 && mAllowEmpty )
r++;
return index( r, 0 );
}

Expand All @@ -93,7 +116,11 @@ void QgsMapLayerModel::removeLayers( const QStringList& layerIds )

void QgsMapLayerModel::addLayers( const QList<QgsMapLayer *>& layers )
{
beginInsertRows( QModelIndex(), mLayers.count(), mLayers.count() + layers.count() - 1 );
int offset = 0;
if ( mAllowEmpty )
offset++;

beginInsertRows( QModelIndex(), mLayers.count() + offset, mLayers.count() + layers.count() - 1 + offset );
Q_FOREACH ( QgsMapLayer* layer, layers )
{
mLayers.append( layer );
Expand All @@ -104,9 +131,17 @@ void QgsMapLayerModel::addLayers( const QList<QgsMapLayer *>& layers )

QModelIndex QgsMapLayerModel::index( int row, int column, const QModelIndex &parent ) const
{
int offset = 0;
if ( mAllowEmpty )
offset++;

if ( hasIndex( row, column, parent ) )
{
return createIndex( row, column, mLayers[row] );
QgsMapLayer* layer = nullptr;
if ( row - offset >= 0 && row - offset < mLayers.count() )
layer = mLayers.at( row - offset );

return createIndex( row, column, layer );
}

return QModelIndex();
Expand All @@ -122,7 +157,10 @@ QModelIndex QgsMapLayerModel::parent( const QModelIndex &child ) const

int QgsMapLayerModel::rowCount( const QModelIndex &parent ) const
{
return parent.isValid() ? 0 : mLayers.length();
if ( parent.isValid() )
return 0;

return ( mAllowEmpty ? 1 : 0 ) + mLayers.length();
}

int QgsMapLayerModel::columnCount( const QModelIndex &parent ) const
Expand All @@ -134,35 +172,58 @@ int QgsMapLayerModel::columnCount( const QModelIndex &parent ) const

QVariant QgsMapLayerModel::data( const QModelIndex &index, int role ) const
{
if ( !index.isValid() || !index.internalPointer() )
bool isEmpty = index.row() == 0 && mAllowEmpty;

if ( !index.isValid() )
return QVariant();

if ( role == Qt::DisplayRole )
{
if ( index.row() == 0 && mAllowEmpty )
return QVariant();

QgsMapLayer* layer = static_cast<QgsMapLayer*>( index.internalPointer() );
return layer->name();
return layer ? layer->name() : QVariant();
}

if ( role == LayerIdRole )
{
if ( isEmpty )
return QVariant();

QgsMapLayer* layer = static_cast<QgsMapLayer*>( index.internalPointer() );
return layer->id();
return layer ? layer->id() : QVariant();
}

if ( role == LayerRole )
{
if ( isEmpty )
return QVariant();

return QVariant::fromValue<QgsMapLayer*>( static_cast<QgsMapLayer*>( index.internalPointer() ) );
}

if ( role == IsEmptyRole )
return isEmpty;

if ( role == Qt::CheckStateRole && mItemCheckable )
{
if ( isEmpty )
return QVariant();

QgsMapLayer* layer = static_cast<QgsMapLayer*>( index.internalPointer() );
return mLayersChecked[layer->id()];
return layer ? mLayersChecked[layer->id()] : QVariant();
}

if ( role == Qt::DecorationRole )
{
if ( isEmpty )
return QVariant();

QgsMapLayer* layer = static_cast<QgsMapLayer*>( index.internalPointer() );
if ( !layer )
return QVariant();

QgsMapLayer::LayerType type = layer->type();
if ( role == Qt::DecorationRole )
{
Expand Down Expand Up @@ -232,8 +293,10 @@ Qt::ItemFlags QgsMapLayerModel::flags( const QModelIndex &index ) const
return 0;
}

bool isEmpty = index.row() == 0 && mAllowEmpty;

Qt::ItemFlags flags = Qt::ItemIsEnabled | Qt::ItemIsSelectable;
if ( mItemCheckable )
if ( mItemCheckable && !isEmpty )
{
flags |= Qt::ItemIsUserCheckable;
}
Expand All @@ -243,7 +306,9 @@ Qt::ItemFlags QgsMapLayerModel::flags( const QModelIndex &index ) const

bool QgsMapLayerModel::setData( const QModelIndex &index, const QVariant &value, int role )
{
if ( role == Qt::CheckStateRole )
bool isEmpty = index.row() == 0 && mAllowEmpty;

if ( role == Qt::CheckStateRole && !isEmpty )
{
QgsMapLayer* layer = static_cast<QgsMapLayer*>( index.internalPointer() );
mLayersChecked[layer->id()] = ( Qt::CheckState )value.toInt();
Expand Down
20 changes: 19 additions & 1 deletion src/core/qgsmaplayermodel.h
Expand Up @@ -39,6 +39,7 @@ class CORE_EXPORT QgsMapLayerModel : public QAbstractItemModel
{
LayerIdRole = Qt::UserRole + 1, //!< Stores the map layer ID
LayerRole, //!< Stores pointer to the map layer itself
IsEmptyRole, //!< True if index corresponds to the empty (not set) value
};

/**
Expand All @@ -61,6 +62,20 @@ class CORE_EXPORT QgsMapLayerModel : public QAbstractItemModel
*/
void checkAll( Qt::CheckState checkState );

/**
* Sets whether an optional empty layer ("not set") option is present in the model.
* @see allowEmptyLayer()
* @note added in QGIS 3.0
*/
void setAllowEmptyLayer( bool allowEmpty );

/**
* Returns true if the model allows the empty layer ("not set") choice.
* @see setAllowEmptyLayer()
* @note added in QGIS 3.0
*/
bool allowEmptyLayer() const { return mAllowEmpty; }

/**
* @brief layersChecked returns the list of layers which are checked (or unchecked)
*/
Expand All @@ -73,7 +88,6 @@ class CORE_EXPORT QgsMapLayerModel : public QAbstractItemModel
*/
QModelIndex indexFromLayer( QgsMapLayer* layer ) const;


protected slots:
void removeLayers( const QStringList& layerIds );
void addLayers( const QList<QgsMapLayer*>& layers );
Expand All @@ -100,6 +114,10 @@ class CORE_EXPORT QgsMapLayerModel : public QAbstractItemModel

bool setData( const QModelIndex &index, const QVariant &value, int role ) override;
Qt::ItemFlags flags( const QModelIndex &index ) const override;

private:

bool mAllowEmpty;
};

#endif // QGSMAPLAYERMODEL_H
10 changes: 10 additions & 0 deletions src/core/qgsmaplayerproxymodel.cpp
Expand Up @@ -77,6 +77,10 @@ bool QgsMapLayerProxyModel::filterAcceptsRow( int source_row, const QModelIndex
return true;

QModelIndex index = sourceModel()->index( source_row, 0, source_parent );

if ( sourceModel()->data( index, QgsMapLayerModel::IsEmptyRole ).toBool() )
return true;

QgsMapLayer* layer = static_cast<QgsMapLayer*>( index.internalPointer() );
if ( !layer )
return false;
Expand Down Expand Up @@ -123,6 +127,12 @@ bool QgsMapLayerProxyModel::filterAcceptsRow( int source_row, const QModelIndex

bool QgsMapLayerProxyModel::lessThan( const QModelIndex &left, const QModelIndex &right ) const
{
// empty row is always first
if ( sourceModel()->data( left, QgsMapLayerModel::IsEmptyRole ).toBool() )
return true;
else if ( sourceModel()->data( right, QgsMapLayerModel::IsEmptyRole ).toBool() )
return false;

// default mode is alphabetical order
QString leftStr = sourceModel()->data( left ).toString();
QString rightStr = sourceModel()->data( right ).toString();
Expand Down
10 changes: 10 additions & 0 deletions src/gui/qgsmaplayercombobox.cpp
Expand Up @@ -28,6 +28,16 @@ QgsMapLayerComboBox::QgsMapLayerComboBox( QWidget *parent )
connect( mProxyModel, SIGNAL( rowsRemoved( QModelIndex, int, int ) ), this, SLOT( rowsChanged() ) );
}

void QgsMapLayerComboBox::setAllowEmptyLayer( bool allowEmpty )
{
mProxyModel->sourceLayerModel()->setAllowEmptyLayer( allowEmpty );
}

bool QgsMapLayerComboBox::allowEmptyLayer() const
{
return mProxyModel->sourceLayerModel()->allowEmptyLayer();
}

void QgsMapLayerComboBox::setLayer( QgsMapLayer *layer )
{
if ( !layer )
Expand Down
14 changes: 14 additions & 0 deletions src/gui/qgsmaplayercombobox.h
Expand Up @@ -53,6 +53,20 @@ class GUI_EXPORT QgsMapLayerComboBox : public QComboBox
//! returns the list of excepted layers
QList<QgsMapLayer*> exceptedLayerList() const {return mProxyModel->exceptedLayerList();}

/**
* Sets whether an optional empty layer ("not set") option is shown in the combo box.
* @see allowEmptyLayer()
* @note added in QGIS 3.0
*/
void setAllowEmptyLayer( bool allowEmpty );

/**
* Returns true if the combo box allows the empty layer ("not set") choice.
* @see setAllowEmptyLayer()
* @note added in QGIS 3.0
*/
bool allowEmptyLayer() const;

/** Returns the current layer selected in the combo box.
* @see layer
*/
Expand Down
1 change: 1 addition & 0 deletions tests/src/python/CMakeLists.txt
Expand Up @@ -57,6 +57,7 @@ ADD_PYTHON_TEST(PyQgsGeometryValidator test_qgsgeometryvalidator.py)
ADD_PYTHON_TEST(PyQgsGraduatedSymbolRenderer test_qgsgraduatedsymbolrenderer.py)
ADD_PYTHON_TEST(PyQgsInterval test_qgsinterval.py)
ADD_PYTHON_TEST(PyQgsJSONUtils test_qgsjsonutils.py)
ADD_PYTHON_TEST(PyQgsMapLayerModel test_qgsmaplayermodel.py)
ADD_PYTHON_TEST(PyQgsMapUnitScale test_qgsmapunitscale.py)
ADD_PYTHON_TEST(PyQgsMemoryProvider test_provider_memory.py)
ADD_PYTHON_TEST(PyQgsMultiEditToolButton test_qgsmultiedittoolbutton.py)
Expand Down

0 comments on commit 959f97f

Please sign in to comment.