Skip to content
Permalink
Browse files

Merge pull request #5268 from nyalldawson/flash2

[FEATURE] Flash features
  • Loading branch information
nyalldawson committed Oct 2, 2017
2 parents d922b55 + 5b6f02e commit d1018cbe2be1c8da402c2a2c8413d65bade0c117
@@ -163,6 +163,12 @@ class QgsAttributeForm : QWidget
%Docstring
Emitted when the user chooses to zoom to a filtered set of features.
.. versionadded:: 3.0
%End

void flashFeatures( const QString &filter );
%Docstring
Emitted when the user chooses to flash a filtered set of features.
.. versionadded:: 3.0
%End

public slots:
@@ -232,6 +232,40 @@ Zoom to the next extent (view)
void panToSelected( QgsVectorLayer *layer = 0 );
%Docstring
Pan to the selected features of current (vector) layer keeping same extent.
%End

void flashFeatureIds( QgsVectorLayer *layer, const QgsFeatureIds &ids,
const QColor &startColor = QColor( 255, 0, 0, 255 ), const QColor &endColor = QColor( 255, 0, 0, 0 ),
int flashes = 3, int duration = 500 );
%Docstring
Causes a set of features with matching ``ids`` from a vector ``layer`` to flash
within the canvas.

The ``startColor`` and ``endColor`` can be specified, along with the number of
``flashes`` and ``duration`` of each flash (in milliseconds).

.. note::

If the features or geometries are already available, flashGeometries() is much more efficient.

.. versionadded:: 3.0
.. seealso:: flashGeometries()
%End

void flashGeometries( const QList< QgsGeometry > &geometries, const QgsCoordinateReferenceSystem &crs = QgsCoordinateReferenceSystem(),
const QColor &startColor = QColor( 255, 0, 0, 255 ), const QColor &endColor = QColor( 255, 0, 0, 0 ),
int flashes = 3, int duration = 500 );
%Docstring
Causes a set of ``geometries`` to flash within the canvas.

If ``crs`` is a valid coordinate reference system, the geometries will be automatically
transformed from this CRS to the canvas CRS.

The ``startColor`` and ``endColor`` can be specified, along with the number of
``flashes`` and ``duration`` of each flash (in milliseconds).

.. versionadded:: 3.0
.. seealso:: flashFeatureIds()
%End

void setMapTool( QgsMapTool *mapTool );
@@ -216,19 +216,29 @@ for tracking the mouse while drawing polylines or polygons.
\param rect rectangle in canvas coordinates
%End

void addGeometry( const QgsGeometry &geom, QgsVectorLayer *layer );
void addGeometry( const QgsGeometry &geometry, QgsVectorLayer *layer );
%Docstring
Adds the geometry of an existing feature to a rubberband
This is useful for multi feature highlighting.
As of 2.0, this method does not change the GeometryType any more. You need to set the GeometryType
of the rubberband explicitly by calling reset() or setToGeometry() with appropriate arguments.
setToGeometry() is also to be preferred for backwards-compatibility.

\param geom the geometry object. Will be treated as a collection of vertices.
\param geometry the geometry object. Will be treated as a collection of vertices.
\param layer the layer containing the feature, used for coord transformation to map
crs. In case of 0 pointer, the coordinates are not going to be transformed.
%End

void addGeometry( const QgsGeometry &geometry, const QgsCoordinateReferenceSystem &crs = QgsCoordinateReferenceSystem() );
%Docstring
Adds a ``geometry`` to the rubberband.

If ``crs`` is specified, the geometry will be automatically reprojected from ``crs``
to the canvas CRS.

.. versionadded:: 3.0
%End

void setTranslationOffset( double dx, double dy );
%Docstring
Adds translation to original coordinates (all in map coordinates)
@@ -275,7 +285,7 @@ for tracking the mouse while drawing polylines or polygons.
\param p The QPainter object
%End

void drawShape( QPainter *p, QVector<QPointF> &pts );
void drawShape( QPainter *p, const QVector<QPointF> &pts );
%Docstring
Draws shape of the rubber band.
\param p The QPainter object
@@ -63,6 +63,7 @@ void QgsSelectByFormDialog::setMapCanvas( QgsMapCanvas *canvas )
{
mMapCanvas = canvas;
connect( mForm, &QgsAttributeForm::zoomToFeatures, this, &QgsSelectByFormDialog::zoomToFeatures );
connect( mForm, &QgsAttributeForm::flashFeatures, this, &QgsSelectByFormDialog::flashFeatures );
}

void QgsSelectByFormDialog::zoomToFeatures( const QString &filter )
@@ -112,3 +113,35 @@ void QgsSelectByFormDialog::zoomToFeatures( const QString &filter )
timeout );
}
}

void QgsSelectByFormDialog::flashFeatures( const QString &filter )
{
QgsExpressionContext context( QgsExpressionContextUtils::globalProjectLayerScopes( mLayer ) );

QgsFeatureRequest request = QgsFeatureRequest().setFilterExpression( filter )
.setExpressionContext( context )
.setSubsetOfAttributes( QgsAttributeList() );

QgsFeatureIterator features = mLayer->getFeatures( request );
QgsFeature feat;
QList< QgsGeometry > geoms;
while ( features.nextFeature( feat ) )
{
if ( feat.hasGeometry() )
geoms << feat.geometry();
}

QgsSettings settings;
int timeout = settings.value( QStringLiteral( "qgis/messageTimeout" ), 5 ).toInt();
if ( !geoms.empty() )
{
mMapCanvas->flashGeometries( geoms, mLayer->crs() );
}
else if ( mMessageBar )
{
mMessageBar->pushMessage( QString(),
tr( "No matching features found" ),
QgsMessageBar::INFO,
timeout );
}
}
@@ -64,6 +64,7 @@ class APP_EXPORT QgsSelectByFormDialog : public QDialog
private slots:

void zoomToFeatures( const QString &filter );
void flashFeatures( const QString &filter );

private:

@@ -408,24 +408,24 @@ void QgsAttributeTableFilterModel::generateListOfVisibleFeatures()
QgsRectangle rect = mCanvas->mapSettings().mapToLayerCoordinates( layer(), mCanvas->extent() );
QgsRenderContext renderContext;
renderContext.expressionContext().appendScopes( QgsExpressionContextUtils::globalProjectLayerScopes( layer() ) );
QgsFeatureRenderer *renderer = layer()->renderer();

mFilteredFeatures.clear();

if ( !renderer )
if ( !layer()->renderer() )
{
QgsDebugMsg( "Cannot get renderer" );
return;
}

std::unique_ptr< QgsFeatureRenderer > renderer( layer()->renderer()->clone() );

const QgsMapSettings &ms = mCanvas->mapSettings();
if ( !layer()->isInScaleRange( ms.scale() ) )
{
QgsDebugMsg( "Out of scale limits" );
}
else
{
if ( renderer && renderer->capabilities() & QgsFeatureRenderer::ScaleDependent )
if ( renderer->capabilities() & QgsFeatureRenderer::ScaleDependent )
{
// setup scale
// mapRenderer()->renderContext()->scale is not automatically updated when
@@ -436,7 +436,7 @@ void QgsAttributeTableFilterModel::generateListOfVisibleFeatures()
renderContext.setRendererScale( ms.scale() );
}

filter = renderer && renderer->capabilities() & QgsFeatureRenderer::Filter;
filter = renderer->capabilities() & QgsFeatureRenderer::Filter;
}

renderer->startRender( renderContext, layer()->fields() );
@@ -476,7 +476,7 @@ void QgsAttributeTableFilterModel::generateListOfVisibleFeatures()

features.close();

if ( renderer && renderer->capabilities() & QgsFeatureRenderer::ScaleDependent )
if ( renderer->capabilities() & QgsFeatureRenderer::ScaleDependent )
{
renderer->stopRender( renderContext );
}
@@ -540,6 +540,7 @@ void QgsDualView::viewWillShowContextMenu( QMenu *menu, const QModelIndex &atInd
{
menu->addAction( tr( "Zoom to feature" ), this, SLOT( zoomToCurrentFeature() ) );
menu->addAction( tr( "Pan to feature" ), this, SLOT( panToCurrentFeature() ) );
menu->addAction( tr( "Flash feature" ), this, SLOT( flashCurrentFeature() ) );
}

//add user-defined actions to context menu
@@ -780,6 +781,23 @@ void QgsDualView::panToCurrentFeature()
}
}

void QgsDualView::flashCurrentFeature()
{
QModelIndex currentIndex = mTableView->currentIndex();
if ( !currentIndex.isValid() )
{
return;
}

QgsFeatureIds ids;
ids.insert( mFilterModel->rowToId( currentIndex ) );
QgsMapCanvas *canvas = mFilterModel->mapCanvas();
if ( canvas )
{
canvas->flashFeatureIds( mLayer, ids );
}
}

void QgsDualView::rebuildFullLayerCache()
{
connect( mLayerCache, &QgsVectorLayerCache::progress, this, &QgsDualView::progress, Qt::UniqueConnection );
@@ -328,6 +328,8 @@ class GUI_EXPORT QgsDualView : public QStackedWidget, private Ui::QgsDualViewBas
//! Pans to the active feature
void panToCurrentFeature();

void flashCurrentFeature();

void rebuildFullLayerCache();

private:
@@ -410,6 +410,15 @@ void QgsAttributeForm::searchZoomTo()
emit zoomToFeatures( filter );
}

void QgsAttributeForm::searchFlash()
{
QString filter = createFilterExpression();
if ( filter.isEmpty() )
return;

emit flashFeatures( filter );
}

void QgsAttributeForm::filterAndTriggered()
{
QString filter = createFilterExpression();
@@ -1353,6 +1362,12 @@ void QgsAttributeForm::init()
boxLayout->addWidget( clearButton );
boxLayout->addStretch( 1 );

QPushButton *flashButton = new QPushButton();
flashButton->setSizePolicy( QSizePolicy::Minimum, QSizePolicy::Minimum );
flashButton->setText( tr( "&Flash features" ) );
connect( flashButton, &QToolButton::clicked, this, &QgsAttributeForm::searchFlash );
boxLayout->addWidget( flashButton );

QPushButton *zoomButton = new QPushButton();
zoomButton->setSizePolicy( QSizePolicy::Minimum, QSizePolicy::Minimum );
zoomButton->setText( tr( "&Zoom to features" ) );
@@ -202,6 +202,12 @@ class GUI_EXPORT QgsAttributeForm : public QWidget
*/
void zoomToFeatures( const QString &filter );

/**
* Emitted when the user chooses to flash a filtered set of features.
* \since QGIS 3.0
*/
void flashFeatures( const QString &filter );

public slots:

/**
@@ -263,6 +269,7 @@ class GUI_EXPORT QgsAttributeForm : public QWidget
void filterTriggered();

void searchZoomTo();
void searchFlash();
void searchSetSelection();
void searchAddToSelection();
void searchRemoveFromSelection();
@@ -1058,6 +1058,106 @@ void QgsMapCanvas::panToSelected( QgsVectorLayer *layer )
refresh();
}

void QgsMapCanvas::flashFeatureIds( QgsVectorLayer *layer, const QgsFeatureIds &ids,
const QColor &color1, const QColor &color2,
int flashes, int duration )
{
if ( !layer )
{
return;
}

QList< QgsGeometry > geoms;

QgsFeatureIterator it = layer->getFeatures( QgsFeatureRequest().setFilterFids( ids ).setSubsetOfAttributes( QgsAttributeList() ) );
QgsFeature fet;
while ( it.nextFeature( fet ) )
{
if ( !fet.hasGeometry() )
continue;
geoms << fet.geometry();
}

flashGeometries( geoms, layer->crs(), color1, color2, flashes, duration );
}

void QgsMapCanvas::flashGeometries( const QList<QgsGeometry> &geometries, const QgsCoordinateReferenceSystem &crs, const QColor &color1, const QColor &color2, int flashes, int duration )
{
if ( geometries.isEmpty() )
return;

QgsWkbTypes::GeometryType geomType = QgsWkbTypes::geometryType( geometries.at( 0 ).wkbType() );
QgsRubberBand *rb = new QgsRubberBand( this, geomType );
for ( const QgsGeometry &geom : geometries )
rb->addGeometry( geom, crs );

if ( geomType == QgsWkbTypes::LineGeometry || geomType == QgsWkbTypes::PointGeometry )
{
rb->setWidth( 2 );
rb->setSecondaryStrokeColor( QColor( 255, 255, 255 ) );
}
if ( geomType == QgsWkbTypes::PointGeometry )
rb->setIcon( QgsRubberBand::ICON_CIRCLE );

QColor startColor = color1;
if ( !startColor.isValid() )
{
if ( geomType == QgsWkbTypes::PolygonGeometry )
{
startColor = rb->fillColor();
}
else
{
startColor = rb->strokeColor();
}
startColor.setAlpha( 255 );
}
QColor endColor = color2;
if ( !endColor.isValid() )
{
endColor = startColor;
endColor.setAlpha( 0 );
}


QVariantAnimation *animation = new QVariantAnimation( this );
connect( animation, &QVariantAnimation::finished, this, [animation, rb]
{
animation->deleteLater();
delete rb;
} );
connect( animation, &QPropertyAnimation::valueChanged, this, [rb, geomType]( const QVariant & value )
{
QColor c = value.value<QColor>();
if ( geomType == QgsWkbTypes::PolygonGeometry )
{
rb->setFillColor( c );
}
else
{
rb->setStrokeColor( c );
QColor c = rb->secondaryStrokeColor();
c.setAlpha( c.alpha() );
rb->setSecondaryStrokeColor( c );
}
rb->update();
} );

animation->setDuration( duration * flashes );
animation->setStartValue( endColor );
double midStep = 0.2 / flashes;
for ( int i = 0; i < flashes; ++i )
{
double start = static_cast< double >( i ) / flashes;
animation->setKeyValueAt( start + midStep, startColor );
double end = static_cast< double >( i + 1 ) / flashes;
if ( !qgsDoubleNear( end, 1.0 ) )
animation->setKeyValueAt( end, endColor );
}
animation->setEndValue( endColor );
animation->start();
}

void QgsMapCanvas::keyPressEvent( QKeyEvent *e )
{
if ( mCanvasProperties->mouseButtonDown || mCanvasProperties->panSelectorDown )

0 comments on commit d1018cb

Please sign in to comment.
You can’t perform that action at this time.