Skip to content

Commit

Permalink
[attrtable] Selection model and request filter
Browse files Browse the repository at this point in the history
* With a selection model, the way the attribute table handles selections
  can be customized. E.g. synchronized to layer selection or used to pick
  features.
* With request filters, the visible features on an attribute table can be
  limited. This will effectively reduce the subset of features the attribute
  table works on. Additional filters by means of a proxy model can of course
  further reduce the visible subset subsequently.
  • Loading branch information
m-kuhn committed Oct 7, 2013
1 parent 5e30831 commit 331f71a
Show file tree
Hide file tree
Showing 25 changed files with 585 additions and 131 deletions.
3 changes: 1 addition & 2 deletions python/gui/attributetable/qgsattributetablemodel.sip
Expand Up @@ -174,9 +174,8 @@ class QgsAttributeTableModel : QAbstractTableModel
/**
* Launched when a feature has been added
* @param fid feature id
* @param inOperation guard insertion with beginInsertRows() / endInsertRows()
*/
virtual void featureAdded( QgsFeatureId fid, bool inOperation = true );
virtual void featureAdded( QgsFeatureId fid );

/**
* Launched when layer has been deleted
Expand Down
9 changes: 0 additions & 9 deletions python/gui/attributetable/qgsattributetableview.sip
Expand Up @@ -10,15 +10,6 @@ class QgsAttributeTableView : QTableView

virtual void setModel( QgsAttributeTableFilterModel* filterModel );

/**
* Autocreates the models
* @param layerCache The @link QgsVectorLayerCache @endlink to use ( as backend )
* @param canvas The @link QgsMapCanvas @endlink to use ( for the currently visible features filter )
*
* @deprecated
*/
void setCanvasAndLayerCache( QgsMapCanvas *canvas, QgsVectorLayerCache *layerCache );

protected:
/**
* Called for mouse press events on a table cell.
Expand Down
10 changes: 0 additions & 10 deletions python/gui/attributetable/qgsdualview.sip
Expand Up @@ -31,16 +31,6 @@ class QgsDualView : QStackedWidget
explicit QgsDualView( QWidget* parent = 0 );
virtual ~QgsDualView();

/**
* Has to be called to initialize the dual view.
*
* @param layer The layer which should be used to fetch features
* @param mapCanvas The mapCanvas (used for the FilterMode
* {@link QgsAttributeTableFilterModel::ShowVisible}
* @param myDa Used for attribute dialog creation
*/
void init( QgsVectorLayer* layer, QgsMapCanvas* mapCanvas, QgsDistanceArea myDa );

/**
* Change the current view mode.
*
Expand Down
4 changes: 2 additions & 2 deletions python/gui/attributetable/qgsfeatureselectionmodel.sip
@@ -1,10 +1,10 @@
class QgsFeatureSelectionModel : QItemSelectionModel
{
%TypeHeaderCode
#include <qgsfeatureselectionmodel.h>
#include <attributetable/qgsfeatureselectionmodel.h>
%End
public:
explicit QgsFeatureSelectionModel( QAbstractItemModel* model, QgsFeatureModel* featureModel, QgsVectorLayer* layer, QObject* parent );
explicit QgsFeatureSelectionModel( QAbstractItemModel* model, QgsFeatureModel* featureModel, QgsIFeatureSelectionManager* featureSelectionManager, QObject* parent );

/**
* Enables or disables synchronisation to the {@link QgsVectorLayer}
Expand Down
77 changes: 77 additions & 0 deletions python/gui/attributetable/qgsifeatureselectionmanager.sip
@@ -0,0 +1,77 @@
/***************************************************************************
qgsifeatureselectionmanager.sip
--------------------------------------
Date : 6.6.2013
Copyright : (C) 2013 Matthias Kuhn
Email : matthias dot kuhn at gmx dot ch
***************************************************************************
* *
* 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. *
* *
***************************************************************************/

/**
* Is an interface class to abstract feature selection handling.
*
* e.g. @link{QgsVectorLayer} implements this interface to manage its selections.
*/

class QgsIFeatureSelectionManager : QObject
{
%TypeHeaderCode
#include <qgsifeatureselectionmanager.h>
%End
public:
QgsIFeatureSelectionManager( QObject* parent );

/**
* The number of features that are selected in this layer
*
* @return See description
*/
virtual int selectedFeatureCount() = 0;

/**
* Select features
*
* @param ids Feature ids to select
*/
virtual void select( const QgsFeatureIds& ids ) = 0;

/**
* Deselect features
*
* @param ids Feature ids to deselect
*/
virtual void deselect( const QgsFeatureIds& ids ) = 0;

/**
* Change selection to the new set of features. Dismisses the current selection.
* Will emit the { @link selectionChanged( QgsFeatureIds, QgsFeatureIds, bool ) } signal with the
* clearAndSelect flag set.
*
* @param ids The ids which will be the new selection
*/
virtual void setSelectedFeatures( const QgsFeatureIds& ids ) = 0;

/**
* Return reference to identifiers of selected features
*
* @return A list of { @link QgsFeatureId } 's
* @see selectedFeatures()
*/
virtual const QgsFeatureIds &selectedFeaturesIds() const = 0;

signals:
/**
* This signal is emitted when selection was changed
*
* @param selected Newly selected feature ids
* @param deselected Ids of all features which have previously been selected but are not any more
* @param clearAndSelect In case this is set to true, the old selection was dismissed and the new selection corresponds to selected
*/
void selectionChanged( const QgsFeatureIds selected, const QgsFeatureIds deselected, const bool clearAndSelect );
};
1 change: 1 addition & 0 deletions python/gui/gui.sip
Expand Up @@ -90,6 +90,7 @@
%Include attributetable/qgsfeaturelistviewdelegate.sip
%Include attributetable/qgsfeaturemodel.sip
%Include attributetable/qgsfeatureselectionmodel.sip
%Include attributetable/qgsifeatureselectionmanager.sip

%Include raster/qgsmultibandcolorrendererwidget.sip
%Include raster/qgspalettedrendererwidget.sip
Expand Down
5 changes: 4 additions & 1 deletion src/app/qgsattributetabledialog.cpp
Expand Up @@ -71,15 +71,18 @@ QgsAttributeTableDialog::QgsAttributeTableDialog( QgsVectorLayer *theLayer, QWid
// Initialize the window geometry
restoreGeometry( settings.value( "/Windows/BetterAttributeTable/geometry" ).toByteArray() );

QgsAttributeEditorContext context;

QgsDistanceArea myDa;

myDa.setSourceCrs( mLayer->crs() );
myDa.setEllipsoidalMode( QgisApp::instance()->mapCanvas()->mapRenderer()->hasCrsTransformEnabled() );
myDa.setEllipsoid( QgsProject::instance()->readEntry( "Measure", "/Ellipsoid", GEO_NONE ) );

context.setDistanceArea( myDa );

// Initialize dual view
mMainView->init( mLayer, QgisApp::instance()->mapCanvas(), myDa );
mMainView->init( mLayer, QgisApp::instance()->mapCanvas(), QgsFeatureRequest(), context );

// Initialize filter gui elements
mFilterActionMapper = new QSignalMapper( this );
Expand Down
38 changes: 23 additions & 15 deletions src/gui/CMakeLists.txt
Expand Up @@ -38,15 +38,17 @@ symbology-ng/qgssvgselectorwidget.cpp
symbology-ng/qgslayerpropertieswidget.cpp
symbology-ng/qgssmartgroupeditordialog.cpp

attributetable/qgsattributetabledelegate.cpp
attributetable/qgsattributetablefiltermodel.cpp
attributetable/qgsattributetablemodel.cpp
attributetable/qgsattributetableview.cpp
attributetable/qgsattributetablefiltermodel.cpp
attributetable/qgsattributetabledelegate.cpp
attributetable/qgsfeaturelistview.cpp
attributetable/qgsdualview.cpp
attributetable/qgsfeaturelistmodel.cpp
attributetable/qgsfeaturelistview.cpp
attributetable/qgsfeaturelistviewdelegate.cpp
attributetable/qgsfeatureselectionmodel.cpp
attributetable/qgsdualview.cpp
attributetable/qgsgenericfeatureselectionmanager.cpp
attributetable/qgsvectorlayerselectionmanager.cpp

editorwidgets/core/qgseditorconfigwidget.cpp
editorwidgets/core/qgseditorwidgetfactory.cpp
Expand Down Expand Up @@ -173,15 +175,18 @@ symbology-ng/qgssvgselectorwidget.h
symbology-ng/qgslayerpropertieswidget.h
symbology-ng/qgssmartgroupeditordialog.h

attributetable/qgsattributetableview.h
attributetable/qgsattributetablemodel.h
attributetable/qgsattributetablefiltermodel.h
attributetable/qgsattributetabledelegate.h
attributetable/qgsfeaturelistview.h
attributetable/qgsattributetablefiltermodel.h
attributetable/qgsattributetablemodel.h
attributetable/qgsattributetableview.h
attributetable/qgsdualview.h
attributetable/qgsfeaturelistmodel.h
attributetable/qgsfeatureselectionmodel.h
attributetable/qgsfeaturelistview.h
attributetable/qgsfeaturelistviewdelegate.h
attributetable/qgsdualview.h
attributetable/qgsfeatureselectionmodel.h
attributetable/qgsgenericfeatureselectionmanager.h
attributetable/qgsifeatureselectionmanager.h
attributetable/qgsvectorlayerselectionmanager.h

editorwidgets/core/qgseditorconfigwidget.h
editorwidgets/core/qgseditorwidgetregistry.h
Expand Down Expand Up @@ -296,16 +301,19 @@ qgssvgannotationitem.h
qgscomposerruler.h
qgsdetaileditemdata.h

attributetable/qgsattributetabledelegate.h
attributetable/qgsattributetablefiltermodel.h
attributetable/qgsattributetablemodel.h
attributetable/qgsattributetableview.h
attributetable/qgsattributetablefiltermodel.h
attributetable/qgsattributetabledelegate.h
attributetable/qgsfeaturelistview.h
attributetable/qgsfeaturemodel.h
attributetable/qgsdualview.h
attributetable/qgsfeaturelistmodel.h
attributetable/qgsfeaturelistview.h
attributetable/qgsfeaturelistviewdelegate.h
attributetable/qgsfeaturemodel.h
attributetable/qgsfeatureselectionmodel.h
attributetable/qgsdualview.h
attributetable/qgsgenericfeatureselectionmanager.h
attributetable/qgsifeatureselectionmanager.h
attributetable/qgsvectorlayerselectionmanager.h

editorwidgets/core/qgseditorconfigwidget.h
editorwidgets/core/qgseditorwidgetfactory.h
Expand Down
3 changes: 0 additions & 3 deletions src/gui/attributetable/qgsattributetablefiltermodel.cpp
Expand Up @@ -33,7 +33,6 @@ QgsAttributeTableFilterModel::QgsAttributeTableFilterModel( QgsMapCanvas* canvas
, mFilterMode( ShowAll )
, mSelectedOnTop( false )
{
mMasterSelection = new QItemSelectionModel( this, this );
setSourceModel( sourceModel );
setDynamicSortFilter( true );
setSortRole( QgsAttributeTableModel::SortRole );
Expand Down Expand Up @@ -115,8 +114,6 @@ void QgsAttributeTableFilterModel::setSelectedOnTop( bool selectedOnTop )
void QgsAttributeTableFilterModel::setSourceModel( QgsAttributeTableModel* sourceModel )
{
mTableModel = sourceModel;
delete mMasterSelection;
mMasterSelection = new QItemSelectionModel( sourceModel, this );

QSortFilterProxyModel::setSourceModel( sourceModel );
}
Expand Down
1 change: 0 additions & 1 deletion src/gui/attributetable/qgsattributetablefiltermodel.h
Expand Up @@ -167,7 +167,6 @@ class GUI_EXPORT QgsAttributeTableFilterModel: public QSortFilterProxyModel, pub
QgsMapCanvas* mCanvas;
FilterMode mFilterMode;
bool mSelectedOnTop;
QItemSelectionModel* mMasterSelection;
QgsAttributeTableModel* mTableModel;
};

Expand Down
74 changes: 47 additions & 27 deletions src/gui/attributetable/qgsattributetablemodel.cpp
Expand Up @@ -50,11 +50,11 @@ QgsAttributeTableModel::QgsAttributeTableModel( QgsVectorLayerCache *layerCache,

loadAttributes();

connect( layer(), SIGNAL( attributeValueChanged( QgsFeatureId, int, const QVariant& ) ), this, SLOT( attributeValueChanged( QgsFeatureId, int, const QVariant& ) ) );
connect( layer(), SIGNAL( featureAdded( QgsFeatureId ) ), this, SLOT( featureAdded( QgsFeatureId ) ) );
connect( mLayerCache, SIGNAL( attributeValueChanged( QgsFeatureId, int, const QVariant& ) ), this, SLOT( attributeValueChanged( QgsFeatureId, int, const QVariant& ) ) );
connect( layer(), SIGNAL( featureDeleted( QgsFeatureId ) ), this, SLOT( featureDeleted( QgsFeatureId ) ) );
connect( layer(), SIGNAL( attributeDeleted( int ) ), this, SLOT( attributeDeleted( int ) ) );
connect( layer(), SIGNAL( updatedFields() ), this, SLOT( updatedFields() ) );
connect( mLayerCache, SIGNAL( featureAdded( QgsFeatureId ) ), this, SLOT( featureAdded( QgsFeatureId ) ) );
connect( mLayerCache, SIGNAL( cachedLayerDeleted() ), this, SLOT( layerDeleted() ) );
}

Expand All @@ -77,13 +77,17 @@ bool QgsAttributeTableModel::loadFeatureAtId( QgsFeatureId fid ) const

void QgsAttributeTableModel::featureDeleted( QgsFeatureId fid )
{
prefetchColumnData( -1 ); // Invalidate cached column data
QgsDebugMsg( QString( "(%2) fid: %1" ).arg( fid ).arg( mFeatureRequest.filterType() ) );
mFieldCache.remove( fid );

int row = idToRow( fid );

beginRemoveRows( QModelIndex(), row, row );
removeRow( row );
endRemoveRows();
if ( row != -1 )
{
beginRemoveRows( QModelIndex(), row, row );
removeRow( row );
endRemoveRows();
}
}

bool QgsAttributeTableModel::removeRows( int row, int count, const QModelIndex &parent )
Expand Down Expand Up @@ -126,20 +130,23 @@ bool QgsAttributeTableModel::removeRows( int row, int count, const QModelIndex &
return true;
}

void QgsAttributeTableModel::featureAdded( QgsFeatureId fid, bool newOperation )
void QgsAttributeTableModel::featureAdded( QgsFeatureId fid )
{
prefetchColumnData( -1 ); // Invalidate cached column data
int n = mRowIdMap.size();
if ( newOperation )
QgsDebugMsg( QString( "(%2) fid: %1" ).arg( fid ).arg( mFeatureRequest.filterType() ) );
if ( loadFeatureAtId( fid ) && mFeatureRequest.acceptFeature( mFeat ) )
{
mFieldCache[ fid ] = mFeat.attribute( mCachedField );

int n = mRowIdMap.size();
beginInsertRows( QModelIndex(), n, n );

mIdRowMap.insert( fid, n );
mRowIdMap.insert( n, fid );
mIdRowMap.insert( fid, n );
mRowIdMap.insert( n, fid );

if ( newOperation )
endInsertRows();

reload( index( rowCount() - 1, 0 ), index( rowCount() - 1, columnCount() ) );
reload( index( rowCount() - 1, 0 ), index( rowCount() - 1, columnCount() ) );
}
}

void QgsAttributeTableModel::updatedFields()
Expand Down Expand Up @@ -176,14 +183,33 @@ void QgsAttributeTableModel::layerDeleted()

void QgsAttributeTableModel::attributeValueChanged( QgsFeatureId fid, int idx, const QVariant &value )
{
if ( mCachedField == idx )
mFieldCache[ fid ] = value;

if ( fid == mFeat.id() )
QgsDebugMsg( QString( "(%4) fid: %1, idx: %2, value: %3" ).arg( fid ).arg( idx ).arg( value.toString() ).arg( mFeatureRequest.filterType() ) );
if ( loadFeatureAtId( fid ) )
{
mFeat.setValid( false );
if ( mFeatureRequest.acceptFeature( mFeat ) )
{
if ( !mIdRowMap.contains( fid ) )
{
// Feature changed in such a way, it will be shown now
featureAdded( fid );
}
else
{
mFieldCache[ fid ] = value;
// Update representation
setData( index( idToRow( fid ), fieldCol( idx ) ), value, Qt::EditRole );
}
}
else
{
if ( mIdRowMap.contains( fid ) )
{
// Feature changed such, that it is no longer shown
featureDeleted( fid );
}
// else: we don't care
}
}
setData( index( idToRow( fid ), fieldCol( idx ) ), value, Qt::EditRole );
}

void QgsAttributeTableModel::loadAttributes()
Expand Down Expand Up @@ -582,12 +608,6 @@ Qt::ItemFlags QgsAttributeTableModel::flags( const QModelIndex &index ) const

void QgsAttributeTableModel::reload( const QModelIndex &index1, const QModelIndex &index2 )
{
for ( int row = index1.row(); row <= index2.row(); row++ )
{
QgsFeatureId fid = rowToId( row );
mLayerCache->removeCachedFeature( fid );
}

mFeat.setFeatureId( std::numeric_limits<int>::min() );
emit dataChanged( index1, index2 );
}
Expand Down Expand Up @@ -646,6 +666,6 @@ void QgsAttributeTableModel::prefetchColumnData( int column )
void QgsAttributeTableModel::setRequest( const QgsFeatureRequest& request )
{
mFeatureRequest = request;
if( layer() && !layer()->hasGeometryType() )
if ( layer() && !layer()->hasGeometryType() )
mFeatureRequest.setFlags( mFeatureRequest.flags() | QgsFeatureRequest::NoGeometry );
}
3 changes: 1 addition & 2 deletions src/gui/attributetable/qgsattributetablemodel.h
Expand Up @@ -227,9 +227,8 @@ class GUI_EXPORT QgsAttributeTableModel: public QAbstractTableModel
/**
* Launched when a feature has been added
* @param fid feature id
* @param inOperation guard insertion with beginInsertRows() / endInsertRows()
*/
virtual void featureAdded( QgsFeatureId fid, bool inOperation = true );
virtual void featureAdded( QgsFeatureId fid );

/**
* Launched when layer has been deleted
Expand Down

0 comments on commit 331f71a

Please sign in to comment.