Skip to content

Commit ccaca73

Browse files
committedFeb 24, 2021
[api] Add option for setting a line symbol to use when rendering
a QgsRubberBand
1 parent ad56216 commit ccaca73

File tree

5 files changed

+157
-15
lines changed

5 files changed

+157
-15
lines changed
 

‎python/gui/auto_generated/qgsrubberband.sip.in

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@ Creates a new RubberBand.
6868
:param geometryType: Defines how the data should be drawn onto the screen.
6969
:py:class:`QgsWkbTypes`.LineGeometry, :py:class:`QgsWkbTypes`.PolygonGeometry or :py:class:`QgsWkbTypes`.PointGeometry
7070
%End
71+
~QgsRubberBand();
7172

7273
void setColor( const QColor &color );
7374
%Docstring
@@ -345,6 +346,36 @@ Returns the rubberband as a Geometry
345346
virtual void updatePosition();
346347

347348

349+
QgsSymbol *symbol() const;
350+
%Docstring
351+
Returns the symbol used for rendering the rubberband, if set.
352+
353+
.. seealso:: :py:func:`setSymbol`
354+
355+
.. versionadded:: 3.20
356+
%End
357+
358+
void setSymbol( QgsSymbol *symbol /Transfer/ );
359+
%Docstring
360+
Sets the ``symbol`` used for rendering the rubberband.
361+
362+
Ownership of ``symbol`` is transferred to the rubberband.
363+
364+
.. warning::
365+
366+
Only line symbols are currently supported.
367+
368+
.. note::
369+
370+
Setting a symbol for the rubberband overrides any other appearance setting,
371+
such as the :py:func:`~QgsRubberBand.strokeColor` or :py:func:`~QgsRubberBand.width`.
372+
373+
374+
.. seealso:: :py:func:`setSymbol`
375+
376+
.. versionadded:: 3.20
377+
%End
378+
348379
protected:
349380

350381
virtual void paint( QPainter *p );

‎src/gui/qgsrubberband.cpp

Lines changed: 49 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,9 @@
2020
#include "qgsvectorlayer.h"
2121
#include "qgsproject.h"
2222
#include "qgsrectangle.h"
23+
#include "qgssymbol.h"
24+
#include "qgsrendercontext.h"
25+
2326
#include <QPainter>
2427

2528
QgsRubberBand::QgsRubberBand( QgsMapCanvas *mapCanvas, QgsWkbTypes::GeometryType geometryType )
@@ -43,6 +46,8 @@ QgsRubberBand::QgsRubberBand()
4346
{
4447
}
4548

49+
QgsRubberBand::~QgsRubberBand() = default;
50+
4651
void QgsRubberBand::setColor( const QColor &color )
4752
{
4853
setStrokeColor( color );
@@ -455,26 +460,45 @@ void QgsRubberBand::paint( QPainter *p )
455460
shapes.append( rings );
456461
}
457462

458-
int iterations = mSecondaryPen.color().isValid() ? 2 : 1;
459-
for ( int i = 0; i < iterations; ++i )
463+
if ( QgsLineSymbol *lineSymbol = dynamic_cast< QgsLineSymbol * >( mSymbol.get() ) )
460464
{
461-
if ( i == 0 && iterations > 1 )
462-
{
463-
// first iteration with multi-pen painting, so use secondary pen
464-
mSecondaryPen.setWidth( mPen.width() + 2 );
465-
p->setBrush( Qt::NoBrush );
466-
p->setPen( mSecondaryPen );
467-
}
468-
else
469-
{
470-
// "top" layer, use primary pen/brush
471-
p->setBrush( mBrush );
472-
p->setPen( mPen );
473-
}
465+
QgsRenderContext context( QgsRenderContext::fromQPainter( p ) );
466+
context.setFlag( QgsRenderContext::Antialiasing, true );
474467

468+
lineSymbol->startRender( context );
475469
for ( const QVector<QPolygonF> &shape : qgis::as_const( shapes ) )
476470
{
477471
drawShape( p, shape );
472+
for ( const QPolygonF &ring : shape )
473+
{
474+
lineSymbol->renderPolyline( ring, nullptr, context );
475+
}
476+
}
477+
lineSymbol->stopRender( context );
478+
}
479+
else
480+
{
481+
int iterations = mSecondaryPen.color().isValid() ? 2 : 1;
482+
for ( int i = 0; i < iterations; ++i )
483+
{
484+
if ( i == 0 && iterations > 1 )
485+
{
486+
// first iteration with multi-pen painting, so use secondary pen
487+
mSecondaryPen.setWidth( mPen.width() + 2 );
488+
p->setBrush( Qt::NoBrush );
489+
p->setPen( mSecondaryPen );
490+
}
491+
else
492+
{
493+
// "top" layer, use primary pen/brush
494+
p->setBrush( mBrush );
495+
p->setPen( mPen );
496+
}
497+
498+
for ( const QVector<QPolygonF> &shape : qgis::as_const( shapes ) )
499+
{
500+
drawShape( p, shape );
501+
}
478502
}
479503
}
480504
}
@@ -630,6 +654,16 @@ void QgsRubberBand::updateRect()
630654
setRect( rect );
631655
}
632656

657+
QgsSymbol *QgsRubberBand::symbol() const
658+
{
659+
return mSymbol.get();
660+
}
661+
662+
void QgsRubberBand::setSymbol( QgsSymbol *symbol )
663+
{
664+
mSymbol.reset( symbol );
665+
}
666+
633667
void QgsRubberBand::updatePosition()
634668
{
635669
// re-compute rectangle

‎src/gui/qgsrubberband.h

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030

3131
class QgsVectorLayer;
3232
class QPaintEvent;
33+
class QgsSymbol;
3334

3435
#ifdef SIP_RUN
3536
% ModuleHeaderCode
@@ -132,6 +133,7 @@ class GUI_EXPORT QgsRubberBand : public QgsMapCanvasItem
132133
* QgsWkbTypes::LineGeometry, QgsWkbTypes::PolygonGeometry or QgsWkbTypes::PointGeometry
133134
*/
134135
QgsRubberBand( QgsMapCanvas *mapCanvas SIP_TRANSFERTHIS, QgsWkbTypes::GeometryType geometryType = QgsWkbTypes::LineGeometry );
136+
~QgsRubberBand() override;
135137

136138
/**
137139
* Sets the color for the rubberband.
@@ -385,6 +387,29 @@ class GUI_EXPORT QgsRubberBand : public QgsMapCanvasItem
385387

386388
void updatePosition() override;
387389

390+
/**
391+
* Returns the symbol used for rendering the rubberband, if set.
392+
*
393+
* \see setSymbol()
394+
* \since QGIS 3.20
395+
*/
396+
QgsSymbol *symbol() const;
397+
398+
/**
399+
* Sets the \a symbol used for rendering the rubberband.
400+
*
401+
* Ownership of \a symbol is transferred to the rubberband.
402+
*
403+
* \warning Only line symbols are currently supported.
404+
*
405+
* \note Setting a symbol for the rubberband overrides any other appearance setting,
406+
* such as the strokeColor() or width().
407+
*
408+
* \see setSymbol()
409+
* \since QGIS 3.20
410+
*/
411+
void setSymbol( QgsSymbol *symbol SIP_TRANSFER );
412+
388413
protected:
389414

390415
/**
@@ -423,6 +448,8 @@ class GUI_EXPORT QgsRubberBand : public QgsMapCanvasItem
423448
std::unique_ptr<QSvgRenderer> mSvgRenderer;
424449
QPoint mSvgOffset;
425450

451+
std::unique_ptr< QgsSymbol > mSymbol;
452+
426453
/**
427454
* Nested lists used for multitypes
428455
*/

‎tests/src/gui/testqgsrubberband.cpp

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@
2525
#include <qgsvectorlayer.h>
2626
#include <qgsrubberband.h>
2727
#include <qgslogger.h>
28+
#include "qgssymbol.h"
29+
#include "qgsrenderchecker.h"
2830

2931
class TestQgsRubberband : public QObject
3032
{
@@ -42,12 +44,14 @@ class TestQgsRubberband : public QObject
4244
void testBoundingRect(); //test for #12392
4345
void testVisibility(); //test for 12486
4446
void testClose(); //test closing geometry
47+
void testSymbolRender();
4548

4649
private:
4750
QgsMapCanvas *mCanvas = nullptr;
4851
QgsVectorLayer *mPolygonLayer = nullptr;
4952
QString mTestDataDir;
5053
QgsRubberBand *mRubberband = nullptr;
54+
QString mReport;
5155
};
5256

5357
void TestQgsRubberband::initTestCase()
@@ -75,6 +79,7 @@ void TestQgsRubberband::initTestCase()
7579
mCanvas->hide();
7680

7781
mRubberband = nullptr;
82+
mReport += QLatin1String( "<h1>Rubberband Tests</h1>\n" );
7883
}
7984

8085
void TestQgsRubberband::cleanupTestCase()
@@ -83,6 +88,15 @@ void TestQgsRubberband::cleanupTestCase()
8388
delete mPolygonLayer;
8489
delete mCanvas;
8590

91+
QString myReportFile = QDir::tempPath() + "/qgistest.html";
92+
QFile myFile( myReportFile );
93+
if ( myFile.open( QIODevice::WriteOnly | QIODevice::Append ) )
94+
{
95+
QTextStream myQTextStream( &myFile );
96+
myQTextStream << mReport;
97+
myFile.close();
98+
}
99+
86100
QgsApplication::exitQgis();
87101
}
88102

@@ -216,6 +230,42 @@ void TestQgsRubberband::testClose()
216230
QCOMPARE( r.partSize( 0 ), 4 );
217231
}
218232

233+
void TestQgsRubberband::testSymbolRender()
234+
{
235+
std::unique_ptr< QgsMapCanvas > canvas = qgis::make_unique< QgsMapCanvas >();
236+
canvas->setDestinationCrs( QgsCoordinateReferenceSystem( QStringLiteral( "EPSG:4326" ) ) );
237+
canvas->setFrameStyle( 0 );
238+
canvas->resize( 600, 400 );
239+
canvas->setExtent( QgsRectangle( 10, 30, 20, 35 ) );
240+
canvas->show();
241+
242+
QgsRubberBand r( canvas.get(), QgsWkbTypes::LineGeometry );
243+
r.addGeometry( QgsGeometry::fromWkt( QStringLiteral( "LineString( 12 32, 18 33)" ) ) );
244+
245+
std::unique_ptr< QgsLineSymbol > lineSymbol( QgsLineSymbol::createSimple(
246+
{
247+
{ QStringLiteral( "line_color" ), QStringLiteral( "#0000ff" ) },
248+
{ QStringLiteral( "line_width" ), QStringLiteral( "3" )},
249+
{ QStringLiteral( "capstyle" ), QStringLiteral( "round" )}
250+
} ) );
251+
r.setSymbol( lineSymbol.release() );
252+
253+
QPixmap pixmap( canvas->size() );
254+
QPainter painter( &pixmap );
255+
canvas->render( &painter );
256+
painter.end();
257+
QString destFile = QDir::tempPath() + QStringLiteral( "/rubberband_line_symbol.png" );
258+
pixmap.save( destFile );
259+
260+
QgsRenderChecker checker;
261+
checker.setControlPathPrefix( QStringLiteral( "rubberband" ) );
262+
checker.setControlName( QStringLiteral( "expected_line_symbol" ) );
263+
checker.setRenderedImage( destFile );
264+
bool result = checker.compareImages( QStringLiteral( "expected_line_symbol" ) );
265+
mReport += checker.report();
266+
QVERIFY( result );
267+
}
268+
219269

220270
QGSTEST_MAIN( TestQgsRubberband )
221271
#include "testqgsrubberband.moc"
Loading

0 commit comments

Comments
 (0)
Please sign in to comment.