From af532ecdf396ade589d40cb2cc13959418cdcd68 Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Mon, 13 Mar 2017 09:53:09 +1000 Subject: [PATCH] Improved docs for QgsMapCanvas::setTheme, handle setLayers and setLayerStyleOverrides when canvas is following a map theme --- python/gui/qgsmapcanvas.sip | 266 +++----------------------- src/gui/qgsmapcanvas.cpp | 29 ++- src/gui/qgsmapcanvas.h | 52 ++++- tests/src/python/test_qgsmapcanvas.py | 22 +++ 4 files changed, 118 insertions(+), 251 deletions(-) diff --git a/python/gui/qgsmapcanvas.sip b/python/gui/qgsmapcanvas.sip index f67e839993e..aaf7ff34735 100644 --- a/python/gui/qgsmapcanvas.sip +++ b/python/gui/qgsmapcanvas.sip @@ -18,298 +18,82 @@ class QgsMapCanvas : QGraphicsView public: //! Constructor - QgsMapCanvas( QWidget * parent /TransferThis/ = 0 ); - + QgsMapCanvas( QWidget *parent /TransferThis/ = 0 ); ~QgsMapCanvas(); - - //! Returns the magnification factor - //! @note added in 2.16 double magnificationFactor() const; - - //! Set list of layers that should be shown in the canvas - //! @note added in 3.0 - void setLayers( const QList& layers ); - - void setCurrentLayer( QgsMapLayer* layer ); - - //! Get access to properties used for map rendering - //! @note added in 2.4 - const QgsMapSettings& mapSettings() const /KeepReference/; - - //! sets destination coordinate reference system - //! @note added in 2.4 - void setDestinationCrs( const QgsCoordinateReferenceSystem& crs ); - - //! Get access to the labeling results (may be null) - //! @note added in 2.4 - const QgsLabelingResults* labelingResults() const; - - //! Set whether to cache images of rendered layers - //! @note added in 2.4 + void setLayers( const QList &layers ); + void setCurrentLayer( QgsMapLayer *layer ); + const QgsMapSettings &mapSettings() const /KeepReference/; + void setDestinationCrs( const QgsCoordinateReferenceSystem &crs ); + const QgsLabelingResults *labelingResults() const; void setCachingEnabled( bool enabled ); - - //! Check whether images of rendered layers are curerently being cached - //! @note added in 2.4 bool isCachingEnabled() const; - - //! Make sure to remove any rendered images from cache (does nothing if cache is not enabled) - //! @note added in 2.4 void clearCache(); - - //! Reload all layers, clear the cache and refresh the canvas - //! @note added in 2.9 void refreshAllLayers(); - - //! Set whether the layers are rendered in parallel or sequentially - //! @note added in 2.4 void setParallelRenderingEnabled( bool enabled ); - - //! Check whether the layers are rendered in parallel or sequentially - //! @note added in 2.4 bool isParallelRenderingEnabled() const; - - //! Set how often map preview should be updated while it is being rendered (in milliseconds) - //! @note added in 2.4 void setMapUpdateInterval( int timeMilliseconds ); - - //! Find out how often map preview should be updated while it is being rendered (in milliseconds) - //! @note added in 2.4 int mapUpdateInterval() const; - - //! Get the last reported scale of the canvas double scale(); - - //! Returns the mapUnitsPerPixel (map units per pixel) for the canvas double mapUnitsPerPixel() const; - - //! Returns the current zoom extent of the map canvas QgsRectangle extent() const; - //! Returns the combined extent for all layers on the map canvas QgsRectangle fullExtent() const; - - //! Set the extent of the map canvas void setExtent( const QgsRectangle &r, bool magnified = false ); - - //! Get the current map canvas rotation in clockwise degrees - //! @note added in 2.8 double rotation() const; - - //! Set the rotation of the map canvas in clockwise degrees - //! @note added in 2.8 void setRotation( double degrees ); - - //! Set the center of the map canvas, in geographical coordinates - //! @note added in 2.8 - void setCenter( const QgsPoint& center ); - - //! Get map center, in geographical coordinates - //! @note added in 2.8 + void setCenter( const QgsPoint ¢er ); QgsPoint center() const; - - //! Zoom to the full extent of all layers void zoomToFullExtent(); - - //! Zoom to the previous extent (view) void zoomToPreviousExtent(); - - //! Zoom to the next extent (view) void zoomToNextExtent(); - - // ! Clears the list of extents and sets current extent as first item void clearExtentHistory(); - - /** Zoom to the extent of the selected features of current (vector) layer. - * @param layer optionally specify different than current layer - */ - void zoomToSelected( QgsVectorLayer* layer = 0 ); - - /** Set canvas extent to the bounding box of a set of features - @param layer the vector layer - @param ids the feature ids*/ - void zoomToFeatureIds( QgsVectorLayer* layer, const QgsFeatureIds& ids ); - - /** Centers canvas extent to feature ids - @param layer the vector layer - @param ids the feature ids*/ - void panToFeatureIds( QgsVectorLayer* layer, const QgsFeatureIds& ids ); - - //! Pan to the selected features of current (vector) layer keeping same extent. - void panToSelected( QgsVectorLayer* layer = 0 ); - - //! \brief Sets the map tool currently being used on the canvas - void setMapTool( QgsMapTool* mapTool ); - - /** \brief Unset the current map tool or last non zoom tool - * - * This is called from destructor of map tools to make sure - * that this map tool won't be used any more. - * You don't have to call it manualy, QgsMapTool takes care of it. - */ - void unsetMapTool( QgsMapTool* mapTool ); - - //! Returns the currently active tool - QgsMapTool* mapTool(); - - //! Write property of QColor bgColor. - void setCanvasColor( const QColor & _newVal ); - //! Read property of QColor bgColor. + void zoomToSelected( QgsVectorLayer *layer = 0 ); + void zoomToFeatureIds( QgsVectorLayer *layer, const QgsFeatureIds &ids ); + void panToFeatureIds( QgsVectorLayer *layer, const QgsFeatureIds &ids ); + void panToSelected( QgsVectorLayer *layer = 0 ); + void setMapTool( QgsMapTool *mapTool ); + void unsetMapTool( QgsMapTool *mapTool ); + QgsMapTool *mapTool(); + void setCanvasColor( const QColor &_newVal ); QColor canvasColor() const; - - //! Set color of selected vector features - //! @note added in 2.4 - void setSelectionColor( const QColor& color ); - - //! Emits signal scaleChanged to update scale in main window + void setSelectionColor( const QColor &color ); void updateScale(); - - //! return the map layer at position index in the layer stack QgsMapLayer *layer( int index ); - - //! return number of layers on the map int layerCount() const; - - //! return list of layers within map canvas. - QList layers() const; + QList layers() const; void freeze( bool frozen = true ); bool isFrozen() const; bool renderFlag() const; - - //! Get the current canvas map units QgsUnitTypes::DistanceUnit mapUnits() const; - - //! Getter for stored overrides of styles for layers. - //! @note added in 2.12 QMap layerStyleOverrides() const; - - //! Setter for stored overrides of styles for layers. - //! @note added in 2.12 - void setLayerStyleOverrides( const QMap& overrides ); - + void setLayerStyleOverrides( const QMap &overrides ); void setTheme( const QString &theme ); QString theme() const; const QgsMapToPixel *getCoordinateTransform(); bool isDrawing(); QgsMapLayer *currentLayer(); void setWheelFactor( double factor ); - - //! Zoom to a specific scale void zoomScale( double scale ); - - //! Zoom with the factor supplied. Factor > 1 zooms out, interval (0,1) zooms in - //! If point is given, re-center on it void zoomByFactor( double scaleFactor, const QgsPoint *center = 0 ); - - //! Zooms in/out with a given center void zoomWithCenter( int x, int y, bool zoomIn ); - - //! Zooms to feature extent. Adds a small margin around the extent - //! and does a pan if rect is empty (point extent) - void zoomToFeatureExtent( QgsRectangle& rect ); - - //! Returns whether the scale is locked, so zooming can be performed using magnication. - //! @note added in 2.16 - //! @see setScaleLocked() + void zoomToFeatureExtent( QgsRectangle &rect ); bool scaleLocked() const; - - //! used to determine if anti-aliasing is enabled or not void enableAntiAliasing( bool flag ); - - //! true if antialising is enabled bool antiAliasingEnabled() const; - - //! sets map tile rendering flag void enableMapTileRendering( bool flag ); - - // following 2 methods should be moved elsewhere or changed to private - // currently used by pan map tool - //! Ends pan action and redraws the canvas. void panActionEnd( QPoint releasePoint ); - - //! Called when mouse is moving and pan is activated - void panAction( QMouseEvent * event ); - - //! returns last position of mouse cursor + void panAction( QMouseEvent *event ); QPoint mouseLastXY(); - - /** Enables a preview mode for the map canvas - * @param previewEnabled set to true to enable a preview mode - * @see setPreviewMode - * @note added in 2.3 */ void setPreviewModeEnabled( bool previewEnabled ); - - /** Returns whether a preview mode is enabled for the map canvas - * @returns true if a preview mode is currently enabled - * @see setPreviewModeEnabled - * @see previewMode - * @note added in 2.3 */ bool previewModeEnabled() const; - - /** Sets a preview mode for the map canvas. This setting only has an effect if - * previewModeEnabled is true. - * @param mode preview mode for the canvas - * @see previewMode - * @see setPreviewModeEnabled - * @see previewModeEnabled - * @note added in 2.3 */ void setPreviewMode( QgsPreviewEffect::PreviewMode mode ); - - /** Returns the current preview mode for the map canvas. This setting only has an effect if - * previewModeEnabled is true. - * @returns preview mode for map canvas - * @see setPreviewMode - * @see previewModeEnabled - * @note added in 2.3 */ QgsPreviewEffect::PreviewMode previewMode() const; - - /** Return snapping utility class that is associated with map canvas. - * If no snapping utils instance has been associated previously, an internal will be created for convenience - * (so map tools do not need to test for existence of the instance). - * - * Main canvas in QGIS returns an instance which is always up-to-date with the project's snapping configuration. - * @note added in 2.8 - */ - QgsSnappingUtils* snappingUtils() const; - - /** Assign an instance of snapping utils to the map canvas. - * The instance is not owned by the canvas, so it is possible to use one instance in multiple canvases. - * - * For main canvas in QGIS, do not associate a different instance from the existing one (it is updated from - * the project's snapping configuration). - * @note added in 2.8 - */ - void setSnappingUtils( QgsSnappingUtils* utils ); - - /** Sets an expression context scope for the map canvas. This scope is injected into the expression - * context used for rendering the map, and can be used to apply specific variable overrides for - * expression evaluation for the map canvas render. This method will overwrite the existing expression - * context scope for the canvas. - * @param scope new expression context scope - * @note added in QGIS 2.12 - * @see expressionContextScope() - */ - void setExpressionContextScope( const QgsExpressionContextScope& scope ); - - /** Returns a reference to the expression context scope for the map canvas. This scope is injected - * into the expression context used for rendering the map, and can be used to apply specific variable - * overrides for expression evaluation for the map canvas render. - * @note added in QGIS 2.12 - * @see setExpressionContextScope() - */ - QgsExpressionContextScope& expressionContextScope(); - - /** Returns a const reference to the expression context scope for the map canvas. - * @note added in QGIS 2.12 - * @see setExpressionContextScope() - * @note not available in python bindings - */ - // const QgsExpressionContextScope& expressionContextScope() const; - - /** Sets the segmentation tolerance applied when rendering curved geometries - @param tolerance the segmentation tolerance*/ + QgsSnappingUtils *snappingUtils() const; + void setSnappingUtils( QgsSnappingUtils *utils ); + void setExpressionContextScope( const QgsExpressionContextScope &scope ); + QgsExpressionContextScope &expressionContextScope(); + // const QgsExpressionContextScope &expressionContextScope() const; void setSegmentationTolerance( double tolerance ); - - /** Sets segmentation tolerance type (maximum angle or maximum difference between curve and approximation) - @param type the segmentation tolerance typename*/ void setSegmentationToleranceType( QgsAbstractGeometry::SegmentationToleranceType type ); public slots: diff --git a/src/gui/qgsmapcanvas.cpp b/src/gui/qgsmapcanvas.cpp index ff547c6c0c2..4615f6972d0 100644 --- a/src/gui/qgsmapcanvas.cpp +++ b/src/gui/qgsmapcanvas.cpp @@ -291,6 +291,15 @@ const QgsMapToPixel *QgsMapCanvas::getCoordinateTransform() } void QgsMapCanvas::setLayers( const QList &layers ) +{ + // following a theme => request denied! + if ( !mTheme.isEmpty() ) + return; + + setLayersPrivate( layers ); +} + +void QgsMapCanvas::setLayersPrivate( const QList &layers ) { QList oldLayers = mSettings.layers(); @@ -487,6 +496,12 @@ void QgsMapCanvas::refreshMap() if ( !mTheme.isEmpty() ) { + // IMPORTANT: we MUST set the layer style overrides here! (At the time of writing this + // comment) retrieving layer styles from the theme collection gives an XML snapshot of the + // current state of the style. If we had stored the style overrides earlier (such as in + // mapThemeChanged slot) then this xml could be out of date... + // TODO: if in future QgsMapThemeCollection::mapThemeStyleOverrides is changed to + // just return the style name, we can instead set the overrides in mapThemeChanged and not here mSettings.setLayerStyleOverrides( QgsProject::instance()->mapThemeCollection()->mapThemeStyleOverrides( mTheme ) ); } @@ -530,7 +545,17 @@ void QgsMapCanvas::mapThemeChanged( const QString &theme ) { if ( theme == mTheme ) { - setLayers( QgsProject::instance()->mapThemeCollection()->mapThemeVisibleLayers( mTheme ) ); + // set the canvas layers to match the new layers contained in the map theme + // NOTE: we do this when the theme layers change and not when we are refreshing the map + // as setLayers() sets up necessary connections to handle changes to the layers + setLayersPrivate( QgsProject::instance()->mapThemeCollection()->mapThemeVisibleLayers( mTheme ) ); + // IMPORTANT: we don't set the layer style overrides here! (At the time of writing this + // comment) retrieving layer styles from the theme collection gives an XML snapshot of the + // current state of the style. If changes were made to the style then this xml + // snapshot goes out of sync... + // TODO: if in future QgsMapThemeCollection::mapThemeStyleOverrides is changed to + // just return the style name, we can instead set the overrides here and not in refreshMap() + clearCache(); refresh(); } @@ -1628,7 +1653,7 @@ void QgsMapCanvas::setTheme( const QString &theme ) else { mTheme = theme; - setLayers( QgsProject::instance()->mapThemeCollection()->mapThemeVisibleLayers( mTheme ) ); + setLayersPrivate( QgsProject::instance()->mapThemeCollection()->mapThemeVisibleLayers( mTheme ) ); emit themeChanged( theme ); } } diff --git a/src/gui/qgsmapcanvas.h b/src/gui/qgsmapcanvas.h index 1cea1b165ae..f42bf1b1b5b 100644 --- a/src/gui/qgsmapcanvas.h +++ b/src/gui/qgsmapcanvas.h @@ -85,8 +85,17 @@ class GUI_EXPORT QgsMapCanvas : public QGraphicsView //! @note added in 2.16 double magnificationFactor() const; - //! Set list of layers that should be shown in the canvas - //! @note added in 3.0 + /** + * Sets the list of \a layers that should be shown in the canvas. + * + * If the map canvas has been associated with a map theme via a call + * to setTheme(), then any calls to setLayers() are ignored. It is necessary + * to first clear the theme association by calling setTheme() with an + * empty string before setLayers() calls can be made. + * + * @note added in 3.0 + * @see layers() + */ void setLayers( const QList &layers ); void setCurrentLayer( QgsMapLayer *layer ); @@ -227,7 +236,10 @@ class GUI_EXPORT QgsMapCanvas : public QGraphicsView //! return number of layers on the map int layerCount() const; - //! return list of layers within map canvas. + /** + * Return the list of layers shown within the map canvas. + * @see setLayers() + */ QList layers() const; /** @@ -266,17 +278,39 @@ class GUI_EXPORT QgsMapCanvas : public QGraphicsView */ QgsUnitTypes::DistanceUnit mapUnits() const; - //! Getter for stored overrides of styles for layers. - //! @note added in 2.12 + /** + * Returns the stored overrides of styles for layers. + * @note added in 2.12 + * @see setLayerStyleOverrides(). + */ QMap layerStyleOverrides() const; - //! Setter for stored overrides of styles for layers. - //! @note added in 2.12 + /** + * Sets the stored overrides of styles for rendering layers. + * + * If the map canvas has been associated with a map theme via a call + * to setTheme(), then any calls to setLayerStyleOverrides() are ignored. It is necessary + * to first clear the theme association by calling setTheme() with an + * empty string before setLayerStyleOverrides() calls can be made. + * + * @note added in 2.12 + * @see layerStyleOverrides() + */ void setLayerStyleOverrides( const QMap &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. + * + * When the canvas is associated to a map theme, it will automatically follow + * the layer selection and layer styles from that theme. Calls to setLayers() + * or setLayerStyleOverrides() will have no effect, and canvases associated + * with a QgsLayerTreeMapCanvasBridge will no longer synchronize their + * state with the layer tree. In these cases it is necessary to call + * setTheme() with an empty string to clear the theme association and + * allow map updates with setLayers(), setLayerStyleOverrides(), or via + * QgsLayerTreeMapCanvasBridge. + * * If an empty string is passed then the current theme association will be * cleared. The layers from the previously associated theme will remain * in the canvas, and a call to setLayers() may be necessary to define @@ -571,7 +605,7 @@ class GUI_EXPORT QgsMapCanvas : public QGraphicsView void layerStyleOverridesChanged(); /** - * Emitted when the map theme set for the canvas is changed. + * Emitted when the canvas has been assigned a different map theme. * @see setTheme() * @note added in QGIS 3.0 */ @@ -763,6 +797,8 @@ class GUI_EXPORT QgsMapCanvas : public QGraphicsView @return true in case of success*/ bool boundingBoxOfFeatureIds( const QgsFeatureIds &ids, QgsVectorLayer *layer, QgsRectangle &bbox, QString &errorMsg ) const; + void setLayersPrivate( const QList &layers ); + friend class TestQgsMapCanvas; }; // class QgsMapCanvas diff --git a/tests/src/python/test_qgsmapcanvas.py b/tests/src/python/test_qgsmapcanvas.py index 730b0f28dd2..99b93e6a2ec 100644 --- a/tests/src/python/test_qgsmapcanvas.py +++ b/tests/src/python/test_qgsmapcanvas.py @@ -312,6 +312,7 @@ class TestQgsMapCanvas(unittest.TestCase): self.assertTrue(self.canvasImageCheck('theme3', 'theme3', canvas)) # change the appearance of an active style + layer2.styleManager().addStyleFromLayer('original') layer2.styleManager().addStyleFromLayer('style4') record3.currentStyle = 'style4' record3.usingCurrentStyle = True @@ -336,6 +337,27 @@ class TestQgsMapCanvas(unittest.TestCase): app.processEvents() self.assertTrue(self.canvasImageCheck('theme4', 'theme4', canvas)) + # try setting layers while a theme is in place + canvas.setLayers([layer]) + canvas.refresh() + + # should be no change... setLayers should be ignored if canvas is following a theme! + while not canvas.isDrawing(): + app.processEvents() + while canvas.isDrawing(): + app.processEvents() + self.assertTrue(self.canvasImageCheck('theme4', 'theme4', canvas)) + + # setLayerStyleOverrides while theme is in place + canvas.setLayerStyleOverrides({layer2.id(): 'original'}) + # should be no change... setLayerStyleOverrides should be ignored if canvas is following a theme! + canvas.refresh() + while not canvas.isDrawing(): + app.processEvents() + while canvas.isDrawing(): + app.processEvents() + self.assertTrue(self.canvasImageCheck('theme4', 'theme4', canvas)) + def canvasImageCheck(self, name, reference_image, canvas): self.report += "

Render {}

\n".format(name) temp_dir = QDir.tempPath() + '/'