[api] Add option for setting a line symbol to use when rendering

a QgsRubberBand
This commit is contained in:
Nyall Dawson 2021-02-24 12:38:50 +10:00
parent ad56216f14
commit ccaca736c3
5 changed files with 157 additions and 15 deletions

View File

@ -68,6 +68,7 @@ Creates a new RubberBand.
:param geometryType: Defines how the data should be drawn onto the screen.
:py:class:`QgsWkbTypes`.LineGeometry, :py:class:`QgsWkbTypes`.PolygonGeometry or :py:class:`QgsWkbTypes`.PointGeometry
%End
~QgsRubberBand();
void setColor( const QColor &color );
%Docstring
@ -345,6 +346,36 @@ Returns the rubberband as a Geometry
virtual void updatePosition();
QgsSymbol *symbol() const;
%Docstring
Returns the symbol used for rendering the rubberband, if set.
.. seealso:: :py:func:`setSymbol`
.. versionadded:: 3.20
%End
void setSymbol( QgsSymbol *symbol /Transfer/ );
%Docstring
Sets the ``symbol`` used for rendering the rubberband.
Ownership of ``symbol`` is transferred to the rubberband.
.. warning::
Only line symbols are currently supported.
.. note::
Setting a symbol for the rubberband overrides any other appearance setting,
such as the :py:func:`~QgsRubberBand.strokeColor` or :py:func:`~QgsRubberBand.width`.
.. seealso:: :py:func:`setSymbol`
.. versionadded:: 3.20
%End
protected:
virtual void paint( QPainter *p );

View File

@ -20,6 +20,9 @@
#include "qgsvectorlayer.h"
#include "qgsproject.h"
#include "qgsrectangle.h"
#include "qgssymbol.h"
#include "qgsrendercontext.h"
#include <QPainter>
QgsRubberBand::QgsRubberBand( QgsMapCanvas *mapCanvas, QgsWkbTypes::GeometryType geometryType )
@ -43,6 +46,8 @@ QgsRubberBand::QgsRubberBand()
{
}
QgsRubberBand::~QgsRubberBand() = default;
void QgsRubberBand::setColor( const QColor &color )
{
setStrokeColor( color );
@ -455,26 +460,45 @@ void QgsRubberBand::paint( QPainter *p )
shapes.append( rings );
}
int iterations = mSecondaryPen.color().isValid() ? 2 : 1;
for ( int i = 0; i < iterations; ++i )
if ( QgsLineSymbol *lineSymbol = dynamic_cast< QgsLineSymbol * >( mSymbol.get() ) )
{
if ( i == 0 && iterations > 1 )
{
// first iteration with multi-pen painting, so use secondary pen
mSecondaryPen.setWidth( mPen.width() + 2 );
p->setBrush( Qt::NoBrush );
p->setPen( mSecondaryPen );
}
else
{
// "top" layer, use primary pen/brush
p->setBrush( mBrush );
p->setPen( mPen );
}
QgsRenderContext context( QgsRenderContext::fromQPainter( p ) );
context.setFlag( QgsRenderContext::Antialiasing, true );
lineSymbol->startRender( context );
for ( const QVector<QPolygonF> &shape : qgis::as_const( shapes ) )
{
drawShape( p, shape );
for ( const QPolygonF &ring : shape )
{
lineSymbol->renderPolyline( ring, nullptr, context );
}
}
lineSymbol->stopRender( context );
}
else
{
int iterations = mSecondaryPen.color().isValid() ? 2 : 1;
for ( int i = 0; i < iterations; ++i )
{
if ( i == 0 && iterations > 1 )
{
// first iteration with multi-pen painting, so use secondary pen
mSecondaryPen.setWidth( mPen.width() + 2 );
p->setBrush( Qt::NoBrush );
p->setPen( mSecondaryPen );
}
else
{
// "top" layer, use primary pen/brush
p->setBrush( mBrush );
p->setPen( mPen );
}
for ( const QVector<QPolygonF> &shape : qgis::as_const( shapes ) )
{
drawShape( p, shape );
}
}
}
}
@ -630,6 +654,16 @@ void QgsRubberBand::updateRect()
setRect( rect );
}
QgsSymbol *QgsRubberBand::symbol() const
{
return mSymbol.get();
}
void QgsRubberBand::setSymbol( QgsSymbol *symbol )
{
mSymbol.reset( symbol );
}
void QgsRubberBand::updatePosition()
{
// re-compute rectangle

View File

@ -30,6 +30,7 @@
class QgsVectorLayer;
class QPaintEvent;
class QgsSymbol;
#ifdef SIP_RUN
% ModuleHeaderCode
@ -132,6 +133,7 @@ class GUI_EXPORT QgsRubberBand : public QgsMapCanvasItem
* QgsWkbTypes::LineGeometry, QgsWkbTypes::PolygonGeometry or QgsWkbTypes::PointGeometry
*/
QgsRubberBand( QgsMapCanvas *mapCanvas SIP_TRANSFERTHIS, QgsWkbTypes::GeometryType geometryType = QgsWkbTypes::LineGeometry );
~QgsRubberBand() override;
/**
* Sets the color for the rubberband.
@ -385,6 +387,29 @@ class GUI_EXPORT QgsRubberBand : public QgsMapCanvasItem
void updatePosition() override;
/**
* Returns the symbol used for rendering the rubberband, if set.
*
* \see setSymbol()
* \since QGIS 3.20
*/
QgsSymbol *symbol() const;
/**
* Sets the \a symbol used for rendering the rubberband.
*
* Ownership of \a symbol is transferred to the rubberband.
*
* \warning Only line symbols are currently supported.
*
* \note Setting a symbol for the rubberband overrides any other appearance setting,
* such as the strokeColor() or width().
*
* \see setSymbol()
* \since QGIS 3.20
*/
void setSymbol( QgsSymbol *symbol SIP_TRANSFER );
protected:
/**
@ -423,6 +448,8 @@ class GUI_EXPORT QgsRubberBand : public QgsMapCanvasItem
std::unique_ptr<QSvgRenderer> mSvgRenderer;
QPoint mSvgOffset;
std::unique_ptr< QgsSymbol > mSymbol;
/**
* Nested lists used for multitypes
*/

View File

@ -25,6 +25,8 @@
#include <qgsvectorlayer.h>
#include <qgsrubberband.h>
#include <qgslogger.h>
#include "qgssymbol.h"
#include "qgsrenderchecker.h"
class TestQgsRubberband : public QObject
{
@ -42,12 +44,14 @@ class TestQgsRubberband : public QObject
void testBoundingRect(); //test for #12392
void testVisibility(); //test for 12486
void testClose(); //test closing geometry
void testSymbolRender();
private:
QgsMapCanvas *mCanvas = nullptr;
QgsVectorLayer *mPolygonLayer = nullptr;
QString mTestDataDir;
QgsRubberBand *mRubberband = nullptr;
QString mReport;
};
void TestQgsRubberband::initTestCase()
@ -75,6 +79,7 @@ void TestQgsRubberband::initTestCase()
mCanvas->hide();
mRubberband = nullptr;
mReport += QLatin1String( "<h1>Rubberband Tests</h1>\n" );
}
void TestQgsRubberband::cleanupTestCase()
@ -83,6 +88,15 @@ void TestQgsRubberband::cleanupTestCase()
delete mPolygonLayer;
delete mCanvas;
QString myReportFile = QDir::tempPath() + "/qgistest.html";
QFile myFile( myReportFile );
if ( myFile.open( QIODevice::WriteOnly | QIODevice::Append ) )
{
QTextStream myQTextStream( &myFile );
myQTextStream << mReport;
myFile.close();
}
QgsApplication::exitQgis();
}
@ -216,6 +230,42 @@ void TestQgsRubberband::testClose()
QCOMPARE( r.partSize( 0 ), 4 );
}
void TestQgsRubberband::testSymbolRender()
{
std::unique_ptr< QgsMapCanvas > canvas = qgis::make_unique< QgsMapCanvas >();
canvas->setDestinationCrs( QgsCoordinateReferenceSystem( QStringLiteral( "EPSG:4326" ) ) );
canvas->setFrameStyle( 0 );
canvas->resize( 600, 400 );
canvas->setExtent( QgsRectangle( 10, 30, 20, 35 ) );
canvas->show();
QgsRubberBand r( canvas.get(), QgsWkbTypes::LineGeometry );
r.addGeometry( QgsGeometry::fromWkt( QStringLiteral( "LineString( 12 32, 18 33)" ) ) );
std::unique_ptr< QgsLineSymbol > lineSymbol( QgsLineSymbol::createSimple(
{
{ QStringLiteral( "line_color" ), QStringLiteral( "#0000ff" ) },
{ QStringLiteral( "line_width" ), QStringLiteral( "3" )},
{ QStringLiteral( "capstyle" ), QStringLiteral( "round" )}
} ) );
r.setSymbol( lineSymbol.release() );
QPixmap pixmap( canvas->size() );
QPainter painter( &pixmap );
canvas->render( &painter );
painter.end();
QString destFile = QDir::tempPath() + QStringLiteral( "/rubberband_line_symbol.png" );
pixmap.save( destFile );
QgsRenderChecker checker;
checker.setControlPathPrefix( QStringLiteral( "rubberband" ) );
checker.setControlName( QStringLiteral( "expected_line_symbol" ) );
checker.setRenderedImage( destFile );
bool result = checker.compareImages( QStringLiteral( "expected_line_symbol" ) );
mReport += checker.report();
QVERIFY( result );
}
QGSTEST_MAIN( TestQgsRubberband )
#include "testqgsrubberband.moc"

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB