Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
Attribute table selection: performance improvements
When selecting features with the mouse (click and drag) only redraw
the map canvas, once the mouse is released.
  • Loading branch information
m-kuhn committed Apr 8, 2013
1 parent af4c4be commit 43dfeb5
Show file tree
Hide file tree
Showing 7 changed files with 108 additions and 37 deletions.
16 changes: 15 additions & 1 deletion src/gui/attributetable/qgsattributetablefiltermodel.cpp
Expand Up @@ -179,7 +179,10 @@ void QgsAttributeTableFilterModel::masterSelectionChanged( const QItemSelection
}

// Now emit the signal
layer()->setSelectedFeatures( layer()->selectedFeaturesIds() );
if ( mSyncSelection )
{
layer()->setSelectedFeatures( layer()->selectedFeaturesIds() );
}

connect( layer(), SIGNAL( selectionChanged() ), this, SLOT( selectionChanged() ) );
}
Expand Down Expand Up @@ -321,6 +324,17 @@ QItemSelectionModel* QgsAttributeTableFilterModel::masterSelection()
return mMasterSelection;
}

void QgsAttributeTableFilterModel::disableSelectionSync()
{
mSyncSelection = false;
}

void QgsAttributeTableFilterModel::enableSelectionSync()
{
mSyncSelection = true;
layer()->setSelectedFeatures( layer()->selectedFeaturesIds() );
}

QModelIndex QgsAttributeTableFilterModel::mapToMaster( const QModelIndex &proxyIndex ) const
{
// Master is source
Expand Down
21 changes: 21 additions & 0 deletions src/gui/attributetable/qgsattributetablefiltermodel.h
Expand Up @@ -91,6 +91,11 @@ class GUI_EXPORT QgsAttributeTableFilterModel: public QSortFilterProxyModel
*/
inline QgsVectorLayer *layer() const { return masterModel()->layer(); }

/**
* Returns the layerCache this filter acts on.
*
* @return The layer cache
*/
inline QgsVectorLayerCache *layerCache() const { return masterModel()->layerCache(); }

/**
Expand Down Expand Up @@ -120,6 +125,21 @@ class GUI_EXPORT QgsAttributeTableFilterModel: public QSortFilterProxyModel
*/
QItemSelectionModel* masterSelection();

/**
* Disables selection synchronisation with the map canvas. Changes to the selection in the master
* model are propagated to the layer, but no redraw is requested until @link{enableSelectionSync()}
* is called.
*/
void disableSelectionSync();

/**
* Enables selection synchronisation with the map canvas. Changes to the selection in the master
* are propagated and upon every change, a redraw will be requested. This method will update the
* selection to account for any cached selection change since @link{disableSelectionSync()} was
* called.
*/
void enableSelectionSync();

virtual QModelIndex mapToMaster( const QModelIndex &proxyIndex ) const;

virtual QModelIndex mapFromMaster( const QModelIndex &sourceIndex ) const;
Expand Down Expand Up @@ -194,6 +214,7 @@ class GUI_EXPORT QgsAttributeTableFilterModel: public QSortFilterProxyModel
bool mSelectedOnTop;
QItemSelectionModel* mMasterSelection;
QgsAttributeTableModel* mTableModel;
bool mSyncSelection;
};

#endif
42 changes: 25 additions & 17 deletions src/gui/attributetable/qgsattributetableview.cpp
Expand Up @@ -46,7 +46,7 @@ QgsAttributeTableView::QgsAttributeTableView( QWidget *parent )
setSelectionMode( QAbstractItemView::ExtendedSelection );
setSortingEnabled( true );

connect( verticalHeader(), SIGNAL( sectionClicked( int ) ), SLOT( onVerticalHeaderSectionClicked( int ) ) );
verticalHeader()->viewport()->installEventFilter( this );
}

QgsAttributeTableView::~QgsAttributeTableView()
Expand Down Expand Up @@ -75,6 +75,30 @@ void QgsAttributeTableView::setCanvasAndLayerCache( QgsMapCanvas *canvas, QgsVec
delete filterModel;
}

bool QgsAttributeTableView::eventFilter(QObject *object, QEvent *event)
{
if ( object == verticalHeader()->viewport() )
{

qDebug() << "Event " << event->type();

switch ( event->type() )
{
case QEvent::MouseButtonPress:
mFilterModel->disableSelectionSync();
break;

case QEvent::MouseButtonRelease:
mFilterModel->enableSelectionSync();
break;

default:
break;
}
}
return false;
}

void QgsAttributeTableView::setModel( QgsAttributeTableFilterModel* filterModel )
{
if ( mFilterModel )
Expand Down Expand Up @@ -158,22 +182,6 @@ void QgsAttributeTableView::keyPressEvent( QKeyEvent *event )
}
}

void QgsAttributeTableView::onVerticalHeaderSectionClicked( int logicalIndex )
{
Q_UNUSED( logicalIndex )

QgsFeatureIds selectedFeatures;

QModelIndexList selectedRows = selectionModel()->selectedRows();

foreach ( QModelIndex row, selectedRows )
{
selectedFeatures.insert( mFilterModel->rowToId( row ) );
}

emit selectionChangeFinished( selectedFeatures );
}

void QgsAttributeTableView::onFilterAboutToBeInvalidated()
{
disconnect( selectionModel(), SIGNAL( selectionChanged( QItemSelection, QItemSelection ) ), this, SLOT( onSelectionChanged( QItemSelection, QItemSelection ) ) );
Expand Down
28 changes: 12 additions & 16 deletions src/gui/attributetable/qgsattributetableview.h
Expand Up @@ -63,6 +63,18 @@ class GUI_EXPORT QgsAttributeTableView : public QTableView
*/
void setCanvasAndLayerCache( QgsMapCanvas *canvas, QgsVectorLayerCache *layerCache );

/**
* This event filter is installed on the verticalHeader to intercept mouse press and release
* events. These are used to disable / enable live synchronisation with the map canvas selection
* which can be slow due to recurring canvas repaints. Updating the
*
* @param object The object which is the target of the event.
* @param event The intercepted event
*
* @return Returns always false, so the event gets processed
*/
virtual bool eventFilter( QObject* object, QEvent* event );

protected:
/**
* Called for mouse press events on a table cell.
Expand Down Expand Up @@ -124,23 +136,7 @@ class GUI_EXPORT QgsAttributeTableView : public QTableView

void finished();

/**
* @brief
* Is emitted, after the selection has been changed.
*
* @param selectedFeatures A list of currently selected features.
*/
void selectionChangeFinished( const QgsFeatureIds &selectedFeatures );

public slots:
/**
* Is triggered after a mouse release event on the vertical header.
* Emits a selectionChangeFinished() signal, so the underlying sort filter
* can adapt to the current selection without disturbing the users current interaction.
*
* @param logicalIndex The section's logical index
*/
void onVerticalHeaderSectionClicked( int logicalIndex );
void onFilterAboutToBeInvalidated();
void onFilterInvalidated();
void onSelectionChanged( const QItemSelection& selected, const QItemSelection& deselected );
Expand Down
10 changes: 10 additions & 0 deletions src/gui/attributetable/qgsfeaturelistmodel.cpp
Expand Up @@ -238,3 +238,13 @@ int QgsFeatureListModel::rowCount( const QModelIndex& parent ) const
Q_UNUSED( parent )
return sourceModel()->rowCount();
}

void QgsFeatureListModel::disableSelectionSync()
{
mFilterModel->disableSelectionSync();
}

void QgsFeatureListModel::enableSelectionSync()
{
mFilterModel->enableSelectionSync();
}
15 changes: 15 additions & 0 deletions src/gui/attributetable/qgsfeaturelistmodel.h
Expand Up @@ -85,6 +85,21 @@ class QgsFeatureListModel : public QAbstractProxyModel
virtual int columnCount( const QModelIndex&parent = QModelIndex() ) const;
virtual int rowCount( const QModelIndex& parent = QModelIndex() ) const;

/**
* Disables selection synchronisation with the map canvas. Changes to the selection in the master
* model are propagated to the layer, but no redraw is requested until @link{enableSelectionSync()}
* is called.
*/
void disableSelectionSync();

/**
* Enables selection synchronisation with the map canvas. Changes to the selection in the master
* are propagated and upon every change, a redraw will be requested. This method will update the
* selection to account for any cached selection change since @link{disableSelectionSync()} was
* called.
*/
void enableSelectionSync();

public slots:
void onBeginRemoveRows( const QModelIndex& parent, int first, int last );
void onEndRemoveRows( const QModelIndex& parent, int first, int last );
Expand Down
13 changes: 10 additions & 3 deletions src/gui/attributetable/qgsfeaturelistview.cpp
Expand Up @@ -115,15 +115,22 @@ void QgsFeatureListView::mousePressEvent( QMouseEvent *event )
}
else
{
mModel->disableSelectionSync();
QListView::mousePressEvent( event );
}
}

void QgsFeatureListView::mouseReleaseEvent( QMouseEvent *event )
{
mEditSelectionDrag = false;

QListView::mouseReleaseEvent( event );
if ( mEditSelectionDrag )
{
mEditSelectionDrag = false;
}
else
{
QListView::mouseReleaseEvent( event );
mModel->enableSelectionSync();
}
}

void QgsFeatureListView::editSelectionChanged( QItemSelection deselected, QItemSelection selected )
Expand Down

0 comments on commit 43dfeb5

Please sign in to comment.