Skip to content

Commit

Permalink
[FIX #5525] Attribute table performance for large tables
Browse files Browse the repository at this point in the history
The selection handling performance has been improved a lot
Introduces also more detailed signalling of selection change from QgsVectorLayer
  • Loading branch information
m-kuhn committed Apr 13, 2013
1 parent 5280e21 commit 47c10db
Show file tree
Hide file tree
Showing 27 changed files with 903 additions and 527 deletions.
1 change: 1 addition & 0 deletions .gitignore
Expand Up @@ -41,6 +41,7 @@ doc/INSTALL.tex
scripts/Debug
scripts/RelWithDebInfo
/CMakeLists.txt.user
/CMakeLists.txt.user.*
qgis-test.ctest
i18n/*.qm
.project
Expand Down
49 changes: 42 additions & 7 deletions python/core/qgsvectorlayer.sip
Expand Up @@ -221,8 +221,21 @@ class QgsVectorLayer : QgsMapLayer
/** The number of features that are selected in this layer */
int selectedFeatureCount();

/** Select features found within the search rectangle (in layer's coordinates) */
void select( QgsRectangle & rect, bool lock );
/**
* Select features found within the search rectangle (in layer's coordinates)
*
* @param rect The search rectangle
* @param addToSelection If set to true will not clear before selecting
*/
void select( QgsRectangle & rect, bool addToSelection );

/**
* Modifies the current selection on this layer
*
* @param selectIds Select these ids
* @param deselectIds Deselect these ids
*/
void modifySelection(QgsFeatureIds selectIds, QgsFeatureIds deselectIds );

/** Select not selected features and deselect selected ones */
void invertSelection();
Expand Down Expand Up @@ -769,14 +782,36 @@ class QgsVectorLayer : QgsMapLayer
QVariant maximumValue( int index );

public slots:
/** Select feature by its ID, optionally emit signal selectionChanged() */
void select( QgsFeatureId featureId, bool emitSignal = true );
/**
* Select feature by its ID
*
* @param featureId The id of the feature to select
*/
void select( QgsFeatureId featureId );

/** Deselect feature by its ID, optionally emit signal selectionChanged() */
void deselect( QgsFeatureId featureId, bool emitSignal = true );
/**
* Select features by their ID
*
* @param featureIds The ids of the features to select
*/
void select( QgsFeatureIds featureIds );

/**
* Deselect feature by its ID
*
* @param featureId The id of the feature to deselect
*/
void deselect( QgsFeatureId featureId );

/**
* Deselect features by their ID
*
* @param featureIds The ids of the features to deselect
*/
void deselect( QgsFeatureIds featureIds );

/** Clear selection */
void removeSelection( bool emitSignal = true );
void removeSelection();

void triggerRepaint();

Expand Down
16 changes: 10 additions & 6 deletions src/app/qgsmaptoolselectutils.cpp
Expand Up @@ -173,29 +173,33 @@ void QgsMapToolSelectUtils::setSelectFeatures( QgsMapCanvas* canvas,

QgsDebugMsg( "Number of new selected features: " + QString::number( newSelectedFeatures.size() ) );

QgsFeatureIds layerSelectedFeatures;
if ( doDifference )
{
layerSelectedFeatures = vlayer->selectedFeaturesIds();
QgsFeatureIds layerSelectedFeatures = vlayer->selectedFeaturesIds();

QgsFeatureIds selectedFeatures;
QgsFeatureIds deselectedFeatures;

QgsFeatureIds::const_iterator i = newSelectedFeatures.constEnd();
while ( i != newSelectedFeatures.constBegin() )
{
--i;
if ( layerSelectedFeatures.contains( *i ) )
{
layerSelectedFeatures.remove( *i );
deselectedFeatures.insert( *i );
}
else
{
layerSelectedFeatures.insert( *i );
selectedFeatures.insert( *i );
}
}

vlayer->modifySelection( selectedFeatures, deselectedFeatures );
}
else
{
layerSelectedFeatures = newSelectedFeatures;
vlayer->setSelectedFeatures( newSelectedFeatures );
}
vlayer->setSelectedFeatures( layerSelectedFeatures );

QApplication::restoreOverrideCursor();
}
Expand Down
127 changes: 66 additions & 61 deletions src/core/qgsvectorlayer.cpp
Expand Up @@ -144,6 +144,8 @@ QgsVectorLayer::QgsVectorLayer( QString vectorLayerPath,
//QSettings settings;
//mUpdateThreshold = settings.readNumEntry("Map/updateThreshold", 1000);
}

connect ( this, SIGNAL( selectionChanged(QgsFeatureIds,QgsFeatureIds,bool) ), this, SIGNAL( selectionChanged() ) );
} // QgsVectorLayer ctor


Expand Down Expand Up @@ -680,86 +682,101 @@ void QgsVectorLayer::drawVertexMarker( double x, double y, QPainter& p, QgsVecto
}
}

void QgsVectorLayer::select( QgsFeatureId fid, bool emitSignal )
void QgsVectorLayer::select( const QgsFeatureId& fid )
{
mSelectedFeatureIds.insert( fid );

if ( emitSignal )
{
// invalidate cache
setCacheImage( 0 );
setCacheImage( 0 );
emit selectionChanged( QgsFeatureIds() << fid, QgsFeatureIds(), false );
}

emit selectionChanged();
}
void QgsVectorLayer::select( const QgsFeatureIds& featureIds )
{
mSelectedFeatureIds.unite( featureIds );

setCacheImage( 0 );
emit selectionChanged( featureIds, QgsFeatureIds(), false );
}

void QgsVectorLayer::deselect( QgsFeatureId fid, bool emitSignal )
void QgsVectorLayer::deselect( const QgsFeatureId fid )
{
mSelectedFeatureIds.remove( fid );

if ( emitSignal )
{
// invalidate cache
setCacheImage( 0 );
setCacheImage( 0 );
emit selectionChanged( QgsFeatureIds(), QgsFeatureIds() << fid, false );
}

emit selectionChanged();
}
void QgsVectorLayer::deselect( const QgsFeatureIds& featureIds )
{
mSelectedFeatureIds.subtract( featureIds );

setCacheImage( 0 );
emit selectionChanged( QgsFeatureIds(), featureIds, false );
}

void QgsVectorLayer::select( QgsRectangle & rect, bool lock )
void QgsVectorLayer::select(QgsRectangle & rect, bool addToSelection )
{
// normalize the rectangle
rect.normalize();

if ( !lock )
{
removeSelection( false ); // don't emit signal
}

//select all the elements
QgsFeatureIterator fit = getFeatures( QgsFeatureRequest()
.setFilterRect( rect )
.setFlags( QgsFeatureRequest::ExactIntersect | QgsFeatureRequest::NoGeometry )
.setSubsetOfAttributes( QgsAttributeList() ) );

QgsFeatureIds ids;

QgsFeature f;
while ( fit.nextFeature( f ) )
{
select( f.id(), false ); // don't emit signal (not to redraw it everytime)
ids << f.id();
}

// invalidate cache
setCacheImage( 0 );
if ( !addToSelection )
{
setSelectedFeatures( mSelectedFeatureIds + ids );
}
else
{
select( ids );
}
}

emit selectionChanged(); // now emit signal to redraw layer
void QgsVectorLayer::modifySelection( QgsFeatureIds selectIds, QgsFeatureIds deselectIds )
{
QgsFeatureIds intersectingIds = selectIds & deselectIds;
if ( intersectingIds.count() > 0 )
{
QgsDebugMsg( "Trying to select and deselect the same item at the same time. Unsure what to do. Selecting dubious items." );
}

mSelectedFeatureIds -= deselectIds;
mSelectedFeatureIds += selectIds;

emit selectionChanged( selectIds, deselectIds - intersectingIds, false );
}

void QgsVectorLayer::invertSelection()
{
// copy the ids of selected features to tmp
QgsFeatureIds tmp = mSelectedFeatureIds;

removeSelection( false ); // don't emit signal

QgsFeatureIterator fit = getFeatures( QgsFeatureRequest()
.setFlags( QgsFeatureRequest::NoGeometry )
.setSubsetOfAttributes( QgsAttributeList() ) );

QgsFeatureIds ids;

QgsFeature fet;
while ( fit.nextFeature( fet ) )
{
select( fet.id(), false ); // don't emit signal
ids << fet.id();
}

for ( QgsFeatureIds::iterator iter = tmp.begin(); iter != tmp.end(); ++iter )
{
mSelectedFeatureIds.remove( *iter );
}
ids.subtract( mSelectedFeatureIds );

// invalidate cache
setCacheImage( 0 );

emit selectionChanged();
setSelectedFeatures( ids );
}

void QgsVectorLayer::invertSelectionInRectangle( QgsRectangle & rect )
Expand All @@ -772,39 +789,31 @@ void QgsVectorLayer::invertSelectionInRectangle( QgsRectangle & rect )
.setFlags( QgsFeatureRequest::NoGeometry | QgsFeatureRequest::ExactIntersect )
.setSubsetOfAttributes( QgsAttributeList() ) );

QgsFeatureIds selectIds;
QgsFeatureIds deselectIds;

QgsFeature fet;
while ( fit.nextFeature( fet ) )
{
if ( mSelectedFeatureIds.contains( fet.id() ) )
{
deselect( fet.id(), false ); // don't emit signal
deselectIds << fet.id();
}
else
{
select( fet.id(), false ); // don't emit signal
selectIds << fet.id();
}
}

// invalidate cache
setCacheImage( 0 );

emit selectionChanged();
modifySelection( selectIds, deselectIds );
}

void QgsVectorLayer::removeSelection( bool emitSignal )
void QgsVectorLayer::removeSelection()
{
if ( mSelectedFeatureIds.size() == 0 )
return;

mSelectedFeatureIds.clear();

if ( emitSignal )
{
// invalidate cache
setCacheImage( 0 );

emit selectionChanged();
}
setSelectedFeatures( QgsFeatureIds() );
}

void QgsVectorLayer::triggerRepaint()
Expand Down Expand Up @@ -1267,9 +1276,6 @@ bool QgsVectorLayer::deleteSelectedFeatures()

// invalidate cache
setCacheImage( 0 );

emit selectionChanged();

triggerRepaint();
updateExtents();

Expand Down Expand Up @@ -2547,13 +2553,14 @@ bool QgsVectorLayer::rollBack( bool deleteBuffer )

void QgsVectorLayer::setSelectedFeatures( const QgsFeatureIds& ids )
{
QgsFeatureIds deselectedFeatures = mSelectedFeatureIds - ids;
// TODO: check whether features with these ID exist
mSelectedFeatureIds = ids;

// invalidate cache
setCacheImage( 0 );

emit selectionChanged();
emit selectionChanged( ids, deselectedFeatures, true );
}

int QgsVectorLayer::selectedFeatureCount()
Expand Down Expand Up @@ -2593,14 +2600,12 @@ bool QgsVectorLayer::addFeatures( QgsFeatureList features, bool makeSelected )

if ( makeSelected )
{
mSelectedFeatureIds.clear();
for ( QgsFeatureList::iterator iter = features.begin(); iter != features.end(); ++iter )
mSelectedFeatureIds.insert( iter->id() );
QgsFeatureIds ids;

// invalidate cache
setCacheImage( 0 );
for ( QgsFeatureList::iterator iter = features.begin(); iter != features.end(); ++iter )
ids << iter->id();

emit selectionChanged();
setSelectedFeatures ( ids );
}

return res;
Expand Down

0 comments on commit 47c10db

Please sign in to comment.