Skip to content

Commit

Permalink
Allow setting map canvases to auto follow a map theme
Browse files Browse the repository at this point in the history
  • Loading branch information
nyalldawson committed Mar 12, 2017
1 parent cd7b19c commit 1e6bffe
Show file tree
Hide file tree
Showing 7 changed files with 198 additions and 15 deletions.
20 changes: 6 additions & 14 deletions python/gui/qgsmapcanvas.sip
Expand Up @@ -185,16 +185,11 @@ class QgsMapCanvas : QGraphicsView
//! @note added in 2.12
void setLayerStyleOverrides( const QMap<QString, QString>& overrides );

//! Get the current coordinate transform
const QgsMapToPixel* getCoordinateTransform();

//! Find out whether rendering is in progress
void setTheme( const QString &theme );
QString theme() const;
const QgsMapToPixel *getCoordinateTransform();
bool isDrawing();

//! returns current layer (set by legend widget)
QgsMapLayer* currentLayer();

//! set wheel zoom factor (should be greater than 1)
QgsMapLayer *currentLayer();
void setWheelFactor( double factor );

//! Zoom to a specific scale
Expand Down Expand Up @@ -434,12 +429,9 @@ class QgsMapCanvas : QGraphicsView
//! @note added in 2.8
void currentLayerChanged( QgsMapLayer* layer );

//! Emitted when the configuration of overridden layer styles changes
//! @note added in 2.12
void layerStyleOverridesChanged();

//! emit a message (usually to be displayed in a message bar)
void messageEmitted( const QString& title, const QString& message, QgsMessageBar::MessageLevel = QgsMessageBar::INFO );
void themeChanged( const QString &theme );
void messageEmitted( const QString &title, const QString &message, QgsMessageBar::MessageLevel = QgsMessageBar::INFO );

protected:

Expand Down
32 changes: 32 additions & 0 deletions src/gui/qgsmapcanvas.cpp
Expand Up @@ -63,6 +63,7 @@ email : sherman at mrcc.com
#include "qgsrubberband.h"
#include "qgsvectorlayer.h"
#include "qgscursors.h"
#include "qgsmapthemecollection.h"
#include <cmath>


Expand Down Expand Up @@ -140,6 +141,8 @@ QgsMapCanvas::QgsMapCanvas( QWidget *parent )
connect( QgsProject::instance(), &QgsProject::writeProject,
this, &QgsMapCanvas::writeProject );

connect( QgsProject::instance()->mapThemeCollection(), &QgsMapThemeCollection::mapThemeChanged, this, &QgsMapCanvas::mapThemeChanged );

mSettings.setFlag( QgsMapSettings::DrawEditingInfo );
mSettings.setFlag( QgsMapSettings::UseRenderingOptimization );
mSettings.setFlag( QgsMapSettings::RenderPartialOutput );
Expand Down Expand Up @@ -482,6 +485,16 @@ void QgsMapCanvas::refreshMap()

mSettings.setExpressionContext( expressionContext );

if ( !mTheme.isEmpty() )
{
mSettings.setLayerStyleOverrides( QgsProject::instance()->mapThemeCollection()->mapThemeStyleOverrides( mTheme ) );
mSettings.setLayers( QgsProject::instance()->mapThemeCollection()->mapThemeVisibleLayers( mTheme ) );
}
else
{
mSettings.setLayerStyleOverrides( QMap< QString, QString>() );
}

// create the renderer job
Q_ASSERT( !mJob );
mJobCanceled = false;
Expand Down Expand Up @@ -518,6 +531,16 @@ void QgsMapCanvas::refreshMap()
emit renderStarting();
}

void QgsMapCanvas::mapThemeChanged( const QString &theme )
{
if ( theme == mTheme )
{
setLayers( QgsProject::instance()->mapThemeCollection()->mapThemeVisibleLayers( mTheme ) );
clearCache();
refresh();
}
}


void QgsMapCanvas::rendererJobFinished()
{
Expand Down Expand Up @@ -1595,6 +1618,15 @@ void QgsMapCanvas::setLayerStyleOverrides( const QMap<QString, QString> &overrid
emit layerStyleOverridesChanged();
}

void QgsMapCanvas::setTheme( const QString &theme )
{
if ( mTheme == theme )
return;

mTheme = theme;
clearCache();
emit themeChanged( theme );
}

void QgsMapCanvas::setRenderFlag( bool flag )
{
Expand Down
27 changes: 27 additions & 0 deletions src/gui/qgsmapcanvas.h
Expand Up @@ -72,6 +72,7 @@ class QgsRubberBand;
class GUI_EXPORT QgsMapCanvas : public QGraphicsView
{
Q_OBJECT
Q_PROPERTY( QString theme READ theme WRITE setTheme NOTIFY themeChanged )

public:

Expand Down Expand Up @@ -273,6 +274,21 @@ class GUI_EXPORT QgsMapCanvas : public QGraphicsView
//! @note added in 2.12
void setLayerStyleOverrides( const QMap<QString, QString> &overrides );

/**
* Sets a map \a theme to show in the canvas. The theme name must match
* a theme present in the associated project's QgsMapThemeCollection.
* @note added in QGIS 3.0
* @see theme()
*/
void setTheme( const QString &theme );

/**
* Returns the map's theme shown in the canvas, if set.
* @note added in QGIS 3.0
* @see setTheme()
*/
QString theme() const { return mTheme; }

//! Get the current coordinate transform
const QgsMapToPixel *getCoordinateTransform();

Expand Down Expand Up @@ -472,6 +488,8 @@ class GUI_EXPORT QgsMapCanvas : public QGraphicsView

void refreshMap();

void mapThemeChanged( const QString &theme );

signals:

/** Emits current mouse position
Expand Down Expand Up @@ -548,6 +566,13 @@ class GUI_EXPORT QgsMapCanvas : public QGraphicsView
//! @note added in 2.12
void layerStyleOverridesChanged();

/**
* Emitted when the map theme set for the canvas is changed.
* @see setTheme()
* @note added in QGIS 3.0
*/
void themeChanged( const QString &theme );

//! emit a message (usually to be displayed in a message bar)
void messageEmitted( const QString &title, const QString &message, QgsMessageBar::MessageLevel = QgsMessageBar::INFO );

Expand Down Expand Up @@ -708,6 +733,8 @@ class GUI_EXPORT QgsMapCanvas : public QGraphicsView

QTimer mAutoRefreshTimer;

QString mTheme;

//! Force a resize of the map canvas item
//! @note added in 2.16
void updateMapSize();
Expand Down
134 changes: 133 additions & 1 deletion tests/src/python/test_qgsmapcanvas.py
Expand Up @@ -19,7 +19,11 @@
QgsVectorLayer,
QgsFeature,
QgsGeometry,
QgsMultiRenderChecker)
QgsMultiRenderChecker,
QgsFillSymbol,
QgsSingleSymbolRenderer,
QgsMapThemeCollection,
QgsProject)
from qgis.gui import (QgsMapCanvas)

from qgis.PyQt.QtCore import QDir
Expand Down Expand Up @@ -179,6 +183,134 @@ def testCancelAndDestroy(self):
canvas.stopRendering()
del canvas

def testMapTheme(self):
canvas = QgsMapCanvas()
canvas.setDestinationCrs(QgsCoordinateReferenceSystem(4326))
canvas.setFrameStyle(0)
canvas.resize(600, 400)
self.assertEqual(canvas.width(), 600)
self.assertEqual(canvas.height(), 400)

layer = QgsVectorLayer("Polygon?crs=epsg:4326&field=fldtxt:string",
"layer", "memory")
# add a polygon to layer
f = QgsFeature()
f.setGeometry(QgsGeometry.fromRect(QgsRectangle(5, 25, 25, 45)))
self.assertTrue(layer.dataProvider().addFeatures([f]))

# create a style
sym1 = QgsFillSymbol.createSimple({'color': '#ffb200'})
renderer = QgsSingleSymbolRenderer(sym1)
layer.setRenderer(renderer)

canvas.setLayers([layer])
canvas.setExtent(QgsRectangle(10, 30, 20, 35))
canvas.show()

# need to wait until first redraw can occur (note that we first need to wait till drawing starts!)
while not canvas.isDrawing():
app.processEvents()
while canvas.isDrawing():
app.processEvents()

self.assertTrue(self.canvasImageCheck('theme1', 'theme1', canvas))

# add some styles
layer.styleManager().addStyleFromLayer('style1')
sym2 = QgsFillSymbol.createSimple({'color': '#00b2ff'})
renderer2 = QgsSingleSymbolRenderer(sym2)
layer.setRenderer(renderer2)
layer.styleManager().addStyleFromLayer('style2')

canvas.refresh()
while not canvas.isDrawing():
app.processEvents()
while canvas.isDrawing():
app.processEvents()
self.assertTrue(self.canvasImageCheck('theme2', 'theme2', canvas))

layer.styleManager().setCurrentStyle('style1')
canvas.refresh()
while not canvas.isDrawing():
app.processEvents()
while canvas.isDrawing():
app.processEvents()
self.assertTrue(self.canvasImageCheck('theme1', 'theme1', canvas))

# ok, so all good with setting/rendering map styles
# try setting canvas to a particular theme

# make some themes...
theme1 = QgsMapThemeCollection.MapThemeRecord()
record1 = QgsMapThemeCollection.MapThemeLayerRecord(layer)
record1.currentStyle = 'style1'
record1.usingCurrentStyle = True
theme1.setLayerRecords([record1])

theme2 = QgsMapThemeCollection.MapThemeRecord()
record2 = QgsMapThemeCollection.MapThemeLayerRecord(layer)
record2.currentStyle = 'style2'
record2.usingCurrentStyle = True
theme2.setLayerRecords([record2])

QgsProject.instance().mapThemeCollection().insert('theme1', theme1)
QgsProject.instance().mapThemeCollection().insert('theme2', theme2)

canvas.setTheme('theme2')
canvas.refresh()
while not canvas.isDrawing():
app.processEvents()
while canvas.isDrawing():
app.processEvents()
self.assertTrue(self.canvasImageCheck('theme2', 'theme2', canvas))

canvas.setTheme('theme1')
canvas.refresh()
while not canvas.isDrawing():
app.processEvents()
while canvas.isDrawing():
app.processEvents()
self.assertTrue(self.canvasImageCheck('theme1', 'theme1', canvas))

# add another layer
layer2 = QgsVectorLayer("Polygon?crs=epsg:4326&field=fldtxt:string",
"layer2", "memory")
f = QgsFeature()
f.setGeometry(QgsGeometry.fromRect(QgsRectangle(5, 25, 25, 45)))
self.assertTrue(layer2.dataProvider().addFeatures([f]))

# create a style
sym1 = QgsFillSymbol.createSimple({'color': '#b2ff00'})
renderer = QgsSingleSymbolRenderer(sym1)
layer2.setRenderer(renderer)

# rerender canvas - should NOT show new layer
canvas.refresh()
while not canvas.isDrawing():
app.processEvents()
while canvas.isDrawing():
app.processEvents()
self.assertTrue(self.canvasImageCheck('theme1', 'theme1', canvas))
# test again - this time refresh all layers
canvas.refreshAllLayers()
while not canvas.isDrawing():
app.processEvents()
while canvas.isDrawing():
app.processEvents()
self.assertTrue(self.canvasImageCheck('theme1', 'theme1', canvas))

# add layer 2 to theme1
record3 = QgsMapThemeCollection.MapThemeLayerRecord(layer2)
theme1.setLayerRecords([record3])
QgsProject.instance().mapThemeCollection().update('theme1', theme1)

canvas.refresh()
while not canvas.isDrawing():
app.processEvents()
while canvas.isDrawing():
app.processEvents()
self.assertTrue(self.canvasImageCheck('theme3', 'theme3', canvas))

def canvasImageCheck(self, name, reference_image, canvas):
self.report += "<h2>Render {}</h2>\n".format(name)
temp_dir = QDir.tempPath() + '/'
Expand Down
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.

0 comments on commit 1e6bffe

Please sign in to comment.