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/Debug
scripts/RelWithDebInfo scripts/RelWithDebInfo
/CMakeLists.txt.user /CMakeLists.txt.user
/CMakeLists.txt.user.*
qgis-test.ctest qgis-test.ctest
i18n/*.qm i18n/*.qm
.project .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 */ /** The number of features that are selected in this layer */
int selectedFeatureCount(); 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 */ /** Select not selected features and deselect selected ones */
void invertSelection(); void invertSelection();
Expand Down Expand Up @@ -769,14 +782,36 @@ class QgsVectorLayer : QgsMapLayer
QVariant maximumValue( int index ); QVariant maximumValue( int index );


public slots: 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 */ /** Clear selection */
void removeSelection( bool emitSignal = true ); void removeSelection();


void triggerRepaint(); 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() ) ); QgsDebugMsg( "Number of new selected features: " + QString::number( newSelectedFeatures.size() ) );


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

QgsFeatureIds selectedFeatures;
QgsFeatureIds deselectedFeatures;

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

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


QApplication::restoreOverrideCursor(); 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; //QSettings settings;
//mUpdateThreshold = settings.readNumEntry("Map/updateThreshold", 1000); //mUpdateThreshold = settings.readNumEntry("Map/updateThreshold", 1000);
} }

connect ( this, SIGNAL( selectionChanged(QgsFeatureIds,QgsFeatureIds,bool) ), this, SIGNAL( selectionChanged() ) );
} // QgsVectorLayer ctor } // 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 ); mSelectedFeatureIds.insert( fid );


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


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 ); mSelectedFeatureIds.remove( fid );


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


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 // normalize the rectangle
rect.normalize(); rect.normalize();


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

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


QgsFeatureIds ids;

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


// invalidate cache if ( !addToSelection )
setCacheImage( 0 ); {
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() void QgsVectorLayer::invertSelection()
{ {
// copy the ids of selected features to tmp // copy the ids of selected features to tmp
QgsFeatureIds tmp = mSelectedFeatureIds; QgsFeatureIds tmp = mSelectedFeatureIds;


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

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


QgsFeatureIds ids;

QgsFeature fet; QgsFeature fet;
while ( fit.nextFeature( 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 ) ids.subtract( mSelectedFeatureIds );
{
mSelectedFeatureIds.remove( *iter );
}


// invalidate cache setSelectedFeatures( ids );
setCacheImage( 0 );

emit selectionChanged();
} }


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


QgsFeatureIds selectIds;
QgsFeatureIds deselectIds;

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


// invalidate cache modifySelection( selectIds, deselectIds );
setCacheImage( 0 );

emit selectionChanged();
} }


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


mSelectedFeatureIds.clear(); setSelectedFeatures( QgsFeatureIds() );

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

emit selectionChanged();
}
} }


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


// invalidate cache // invalidate cache
setCacheImage( 0 ); setCacheImage( 0 );

emit selectionChanged();

triggerRepaint(); triggerRepaint();
updateExtents(); updateExtents();


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


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


// invalidate cache // invalidate cache
setCacheImage( 0 ); setCacheImage( 0 );


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


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


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


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


emit selectionChanged(); setSelectedFeatures ( ids );
} }


return res; return res;
Expand Down

0 comments on commit 47c10db

Please sign in to comment.