Skip to content

Commit 738f441

Browse files
authoredMar 15, 2017
Merge pull request #4253 from nyalldawson/multi_canvas2
[FEATURE] Multi canvas/additional map views
2 parents b8c3c13 + 2d37ca9 commit 738f441

24 files changed

+1775
-169
lines changed
 

‎images/images.qrc

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -556,6 +556,9 @@
556556
<file>themes/default/processingAlgorithm.svg</file>
557557
<file>themes/default/processingResult.svg</file>
558558
<file>themes/default/search.svg</file>
559+
<file>themes/default/mActionNewMap.svg</file>
560+
<file>themes/default/mActionMapSettings.svg</file>
561+
<file>themes/default/mActionLockExtent.svg</file>
559562
</qresource>
560563
<qresource prefix="/images/tips">
561564
<file alias="symbol_levels.png">qgis_tips/symbol_levels.png</file>
Lines changed: 98 additions & 0 deletions
Loading
Lines changed: 177 additions & 0 deletions
Loading
Lines changed: 98 additions & 0 deletions
Loading

‎python/gui/qgisinterface.sip

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,17 @@ class QgisInterface : QObject
5858

5959
/* Exposed functions */
6060

61-
//! Zoom to full extent of map layers
61+
virtual QList< QgsMapCanvas* > mapCanvases() = 0;
62+
63+
virtual QgsMapCanvas* createNewMapCanvas( const QString& name ) = 0;
64+
65+
virtual void closeMapCanvas( const QString &name ) = 0;
66+
67+
public slots:
68+
69+
70+
71+
6272
virtual void zoomFull() = 0;
6373

6474
//! Zoom to previous view extent

‎python/gui/qgsmapcanvas.sip

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,10 @@ class QgsMapCanvas : QGraphicsView
9797
void setSegmentationTolerance( double tolerance );
9898
void setSegmentationToleranceType( QgsAbstractGeometry::SegmentationToleranceType type );
9999

100+
QList< QgsMapCanvasAnnotationItem *> annotationItems() const;
101+
bool annotationsVisible() const;
102+
void setAnnotationsVisible( bool visible );
103+
100104
public slots:
101105

102106
//! Repaints the canvas map

‎src/app/CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ SET(QGIS_APP_SRCS
4747
qgslabelinggui.cpp
4848
qgslabelingwidget.cpp
4949
qgsloadstylefromdbdialog.cpp
50+
qgsmapcanvasdockwidget.cpp
5051
qgsmaplayerstyleguiutils.cpp
5152
qgsrulebasedlabelingwidget.cpp
5253
qgssavestyletodbdialog.cpp
@@ -225,6 +226,7 @@ SET (QGIS_APP_MOC_HDRS
225226
qgslabelingwidget.h
226227
qgslabelpropertydialog.h
227228
qgsloadstylefromdbdialog.h
229+
qgsmapcanvasdockwidget.h
228230
qgsmaplayerstyleguiutils.h
229231
qgsrulebasedlabelingwidget.h
230232
qgssavestyletodbdialog.h

‎src/app/qgisapp.cpp

Lines changed: 326 additions & 127 deletions
Large diffs are not rendered by default.

‎src/app/qgisapp.h

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -231,6 +231,36 @@ class APP_EXPORT QgisApp : public QMainWindow, private Ui::MainWindow
231231
//! Get the mapcanvas object from the app
232232
QgsMapCanvas *mapCanvas();
233233

234+
/**
235+
* Returns a list of all map canvases open in the app.
236+
*/
237+
QList< QgsMapCanvas * > mapCanvases();
238+
239+
/**
240+
* Create a new map canvas with the specified unique \a name. The \a isFloating
241+
* and \a dockGeometry arguments can be used to specify an initial floating state
242+
* and widget geometry rect for the dock.
243+
*/
244+
QgsMapCanvas *createNewMapCanvas( const QString &name, bool isFloating = false, const QRect &dockGeometry = QRect(),
245+
bool synced = false, bool showCursor = true, bool scaleSynced = false,
246+
double scaleFactor = 1.0 );
247+
248+
/**
249+
* Closes the additional map canvas with matching \a name.
250+
*/
251+
void closeMapCanvas( const QString &name );
252+
253+
/**
254+
* Closes any additional map canvases. The main map canvas will not
255+
* be affected.
256+
*/
257+
void closeAdditionalMapCanvases();
258+
259+
/**
260+
* Freezes all map canvases (or thaws them if the \a frozen argument is false).
261+
*/
262+
void freezeCanvases( bool frozen = true );
263+
234264
//! Return the messageBar object which allows displaying unobtrusive messages to the user.
235265
QgsMessageBar *messageBar();
236266

@@ -736,6 +766,8 @@ class APP_EXPORT QgisApp : public QMainWindow, private Ui::MainWindow
736766

737767
QMenu *panelMenu() { return mPanelMenu; }
738768

769+
void renameView();
770+
739771
protected:
740772

741773
//! Handle state changes (WindowTitleChange)
@@ -1024,6 +1056,9 @@ class APP_EXPORT QgisApp : public QMainWindow, private Ui::MainWindow
10241056
void showAlignRasterTool();
10251057
void embedLayers();
10261058

1059+
//! Creates a new map canvas view
1060+
void newMapCanvas();
1061+
10271062
//! Create a new empty vector layer
10281063
void newVectorLayer();
10291064
//! Create a new memory layer
@@ -1514,6 +1549,7 @@ class APP_EXPORT QgisApp : public QMainWindow, private Ui::MainWindow
15141549

15151550
//! Returns all annotation items in the canvas
15161551
QList<QgsMapCanvasAnnotationItem *> annotationItems();
1552+
15171553
//! Removes annotation items in the canvas
15181554
void removeAnnotationItems();
15191555

@@ -1551,6 +1587,11 @@ class APP_EXPORT QgisApp : public QMainWindow, private Ui::MainWindow
15511587
*/
15521588
void applyProjectSettingsToCanvas( QgsMapCanvas *canvas );
15531589

1590+
/**
1591+
* Applies global qgis settings to the specified canvas
1592+
*/
1593+
void applyDefaultSettingsToCanvas( QgsMapCanvas *canvas );
1594+
15541595
QgisAppStyleSheet *mStyleSheetBuilder = nullptr;
15551596

15561597
// actions for menus and toolbars -----------------
@@ -1754,6 +1795,7 @@ class APP_EXPORT QgisApp : public QMainWindow, private Ui::MainWindow
17541795
QSplashScreen *mSplash = nullptr;
17551796
//! list of recently opened/saved project files
17561797
QList<QgsWelcomePageItemsModel::RecentProjectData> mRecentProjects;
1798+
17571799
//! Print composers of this project, accessible by id string
17581800
QSet<QgsComposer *> mPrintComposers;
17591801
//! QGIS-internal vector feature clipboard

‎src/app/qgisappinterface.cpp

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -331,6 +331,21 @@ QgsMapCanvas *QgisAppInterface::mapCanvas()
331331
return qgis->mapCanvas();
332332
}
333333

334+
QList<QgsMapCanvas *> QgisAppInterface::mapCanvases()
335+
{
336+
return qgis->mapCanvases();
337+
}
338+
339+
QgsMapCanvas *QgisAppInterface::createNewMapCanvas( const QString &name )
340+
{
341+
return qgis->createNewMapCanvas( name );
342+
}
343+
344+
void QgisAppInterface::closeMapCanvas( const QString &name )
345+
{
346+
qgis->closeMapCanvas( name );
347+
}
348+
334349
QgsLayerTreeMapCanvasBridge *QgisAppInterface::layerTreeCanvasBridge()
335350
{
336351
return qgis->layerTreeCanvasBridge();

‎src/app/qgisappinterface.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -179,6 +179,10 @@ class APP_EXPORT QgisAppInterface : public QgisInterface
179179
//! Return a pointer to the map canvas used by qgisapp
180180
QgsMapCanvas *mapCanvas() override;
181181

182+
QList< QgsMapCanvas * > mapCanvases() override;
183+
QgsMapCanvas *createNewMapCanvas( const QString &name ) override;
184+
virtual void closeMapCanvas( const QString &name ) override;
185+
182186
/**
183187
* Returns a pointer to the layer tree canvas bridge
184188
*

‎src/app/qgsguivectorlayertools.cpp

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -130,15 +130,15 @@ bool QgsGuiVectorLayerTools::stopEditing( QgsVectorLayer *layer, bool allowCance
130130
break;
131131

132132
case QMessageBox::Discard:
133-
QgisApp::instance()->mapCanvas()->freeze( true );
133+
QgisApp::instance()->freezeCanvases();
134134
if ( !layer->rollBack() )
135135
{
136136
QgisApp::instance()->messageBar()->pushMessage( tr( "Error" ),
137137
tr( "Problems during roll back" ),
138138
QgsMessageBar::CRITICAL );
139139
res = false;
140140
}
141-
QgisApp::instance()->mapCanvas()->freeze( false );
141+
QgisApp::instance()->freezeCanvases( false );
142142

143143
layer->triggerRepaint();
144144
break;
@@ -149,9 +149,9 @@ bool QgsGuiVectorLayerTools::stopEditing( QgsVectorLayer *layer, bool allowCance
149149
}
150150
else //layer not modified
151151
{
152-
QgisApp::instance()->mapCanvas()->freeze( true );
152+
QgisApp::instance()->freezeCanvases( true );
153153
layer->rollBack();
154-
QgisApp::instance()->mapCanvas()->freeze( false );
154+
QgisApp::instance()->freezeCanvases( false );
155155
res = true;
156156
layer->triggerRepaint();
157157
}

‎src/app/qgsmapcanvasdockwidget.cpp

Lines changed: 464 additions & 0 deletions
Large diffs are not rendered by default.

‎src/app/qgsmapcanvasdockwidget.h

Lines changed: 176 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,176 @@
1+
/***************************************************************************
2+
qgsmapcanvasdockwidget.h
3+
------------------------
4+
begin : February 2017
5+
copyright : (C) 2017 by Nyall Dawson
6+
email : nyall dot dawson at gmail dot com
7+
***************************************************************************
8+
* *
9+
* This program is free software; you can redistribute it and/or modify *
10+
* it under the terms of the GNU General Public License as published by *
11+
* the Free Software Foundation; either version 2 of the License, or *
12+
* (at your option) any later version. *
13+
* *
14+
***************************************************************************/
15+
#ifndef QGSMAPCANVASDOCKWIDGET_H
16+
#define QGSMAPCANVASDOCKWIDGET_H
17+
18+
#include <ui_qgsmapcanvasdockwidgetbase.h>
19+
20+
#include "qgsdockwidget.h"
21+
#include "qgspoint.h"
22+
#include "qgis_app.h"
23+
#include <QWidgetAction>
24+
#include <QTimer>
25+
#include <memory>
26+
27+
class QgsMapCanvas;
28+
class QgsScaleComboBox;
29+
class QgsDoubleSpinBox;
30+
class QgsStatusBarMagnifierWidget;
31+
class QgsMapToolPan;
32+
class QgsVertexMarker;
33+
class QCheckBox;
34+
35+
/**
36+
* \class QgsMapCanvasDockWidget
37+
* A dock widget with an embedded map canvas, for additional map views.
38+
* \note added in QGIS 3.0
39+
*/
40+
class APP_EXPORT QgsMapCanvasDockWidget : public QgsDockWidget, private Ui::QgsMapCanvasDockWidgetBase
41+
{
42+
Q_OBJECT
43+
public:
44+
explicit QgsMapCanvasDockWidget( const QString &name, QWidget *parent = nullptr );
45+
46+
/**
47+
* Sets the main app map canvas.
48+
*/
49+
void setMainCanvas( QgsMapCanvas *canvas );
50+
51+
/**
52+
* Returns the map canvas contained in the dock widget.
53+
*/
54+
QgsMapCanvas *mapCanvas();
55+
56+
/**
57+
* Sets whether the view center should be synchronized with the main canvas center.
58+
* @see isViewCenterSynchronized()
59+
*/
60+
void setViewCenterSynchronized( bool enabled );
61+
62+
/**
63+
* Returns true if the view extent is synchronized with the main canvas extent.
64+
* @see setViewCenterSynchronized()
65+
*/
66+
bool isViewCenterSynchronized() const;
67+
68+
/**
69+
* Sets whether the cursor position marker is visible.
70+
* @see isCursorMarkerVisible()
71+
*/
72+
void setCursorMarkerVisible( bool visible );
73+
74+
/**
75+
* Returns true if the cursor position marker is visible.
76+
* @see setCursorMarkerVisible()
77+
*/
78+
bool isCursorMarkerVisible() const;
79+
80+
/**
81+
* Returns the scaling factor for main canvas scale to view scale.
82+
* @see setScaleFactor()
83+
* @see isViewScaleSynchronized()
84+
*/
85+
double scaleFactor() const;
86+
87+
/**
88+
* Sets the scaling \a factor for main canvas scale to view scale.
89+
* @see scaleFactor()
90+
* @see setViewScaleSynchronized()
91+
*/
92+
void setScaleFactor( double factor );
93+
94+
/**
95+
* Sets whether the view scale should be synchronized with the main canvas center.
96+
* @see isViewScaleSynchronized()
97+
* @see setScaleFactor()
98+
*/
99+
void setViewScaleSynchronized( bool enabled );
100+
101+
/**
102+
* Returns true if the view scale is synchronized with the main canvas extent.
103+
* @see setViewScaleSynchronized()
104+
* @see scaleFactor()
105+
*/
106+
bool isViewScaleSynchronized() const;
107+
108+
signals:
109+
110+
void renameTriggered();
111+
112+
protected:
113+
114+
void resizeEvent( QResizeEvent *e ) override;
115+
116+
private slots:
117+
118+
void setMapCrs();
119+
void mapExtentChanged();
120+
void mapCrsChanged();
121+
void menuAboutToShow();
122+
void settingsMenuAboutToShow();
123+
void syncMarker( const QgsPoint &p );
124+
void mapScaleChanged();
125+
126+
private:
127+
128+
QgsMapCanvas *mMapCanvas = nullptr;
129+
QgsMapCanvas *mMainCanvas = nullptr;
130+
QMenu *mMenu = nullptr;
131+
QList<QAction *> mMenuPresetActions;
132+
QgsScaleComboBox *mScaleCombo = nullptr;
133+
QgsDoubleSpinBox *mRotationEdit = nullptr;
134+
QgsDoubleSpinBox *mMagnificationEdit = nullptr;
135+
QgsDoubleSpinBox *mScaleFactorWidget = nullptr;
136+
QCheckBox *mSyncScaleCheckBox = nullptr;
137+
bool mBlockScaleUpdate = false;
138+
bool mBlockRotationUpdate = false;
139+
bool mBlockMagnificationUpdate = false;
140+
bool mBlockExtentSync = false;
141+
QgsMapToolPan *mPanTool = nullptr;
142+
QTimer mResizeTimer;
143+
QgsVertexMarker *mXyMarker = nullptr;
144+
void syncViewCenter( QgsMapCanvas *sourceCanvas );
145+
};
146+
147+
/**
148+
* \class QgsMapSettingsAction
149+
* Allows embedding a scale, rotation and other map settings into a menu.
150+
* \note added in QGIS 3.0
151+
*/
152+
153+
class QgsMapSettingsAction: public QWidgetAction
154+
{
155+
Q_OBJECT
156+
157+
public:
158+
159+
QgsMapSettingsAction( QWidget *parent = nullptr );
160+
161+
QgsScaleComboBox *scaleCombo() { return mScaleCombo; }
162+
QgsDoubleSpinBox *rotationSpinBox() { return mRotationWidget; }
163+
QgsDoubleSpinBox *magnifierSpinBox() { return mMagnifierWidget; }
164+
QgsDoubleSpinBox *scaleFactorSpinBox() { return mScaleFactorWidget; }
165+
QCheckBox *syncScaleCheckBox() { return mSyncScaleCheckBox; }
166+
167+
private:
168+
QgsScaleComboBox *mScaleCombo = nullptr;
169+
QgsDoubleSpinBox *mRotationWidget = nullptr;
170+
QgsDoubleSpinBox *mMagnifierWidget = nullptr;
171+
QCheckBox *mSyncScaleCheckBox = nullptr;
172+
QgsDoubleSpinBox *mScaleFactorWidget = nullptr;
173+
};
174+
175+
176+
#endif // QGSMAPCANVASDOCKWIDGET_H

‎src/app/qgsmaptoolannotation.cpp

Lines changed: 4 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -336,24 +336,14 @@ QgsMapCanvasAnnotationItem *QgsMapToolAnnotation::selectedItem() const
336336

337337
QList<QgsMapCanvasAnnotationItem *> QgsMapToolAnnotation::annotationItems() const
338338
{
339-
QList<QgsMapCanvasAnnotationItem *> annotationItemList;
340-
if ( !mCanvas || !mCanvas->scene() )
339+
if ( !mCanvas )
341340
{
342-
return annotationItemList;
341+
return QList<QgsMapCanvasAnnotationItem *>();
343342
}
344-
345-
QList<QGraphicsItem *> itemList = mCanvas->scene()->items();
346-
QList<QGraphicsItem *>::iterator it = itemList.begin();
347-
for ( ; it != itemList.end(); ++it )
343+
else
348344
{
349-
QgsMapCanvasAnnotationItem *aItem = dynamic_cast<QgsMapCanvasAnnotationItem *>( *it );
350-
if ( aItem )
351-
{
352-
annotationItemList.push_back( aItem );
353-
}
345+
return mCanvas->annotationItems();
354346
}
355-
356-
return annotationItemList;
357347
}
358348

359349
void QgsMapToolAnnotation::toggleTextItemVisibilities()

‎src/app/qgsprojectproperties.cpp

Lines changed: 24 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -811,21 +811,26 @@ void QgsProjectProperties::apply()
811811
}
812812

813813
//set the color for selections
814-
QColor myColor = pbnSelectionColor->color();
815-
QgsProject::instance()->writeEntry( QStringLiteral( "Gui" ), QStringLiteral( "/SelectionColorRedPart" ), myColor.red() );
816-
QgsProject::instance()->writeEntry( QStringLiteral( "Gui" ), QStringLiteral( "/SelectionColorGreenPart" ), myColor.green() );
817-
QgsProject::instance()->writeEntry( QStringLiteral( "Gui" ), QStringLiteral( "/SelectionColorBluePart" ), myColor.blue() );
818-
QgsProject::instance()->writeEntry( QStringLiteral( "Gui" ), QStringLiteral( "/SelectionColorAlphaPart" ), myColor.alpha() );
819-
mMapCanvas->setSelectionColor( myColor );
814+
QColor selectionColor = pbnSelectionColor->color();
815+
QgsProject::instance()->writeEntry( QStringLiteral( "Gui" ), QStringLiteral( "/SelectionColorRedPart" ), selectionColor.red() );
816+
QgsProject::instance()->writeEntry( QStringLiteral( "Gui" ), QStringLiteral( "/SelectionColorGreenPart" ), selectionColor.green() );
817+
QgsProject::instance()->writeEntry( QStringLiteral( "Gui" ), QStringLiteral( "/SelectionColorBluePart" ), selectionColor.blue() );
818+
QgsProject::instance()->writeEntry( QStringLiteral( "Gui" ), QStringLiteral( "/SelectionColorAlphaPart" ), selectionColor.alpha() );
819+
820820

821821
//set the color for canvas
822-
myColor = pbnCanvasColor->color();
823-
QgsProject::instance()->writeEntry( QStringLiteral( "Gui" ), QStringLiteral( "/CanvasColorRedPart" ), myColor.red() );
824-
QgsProject::instance()->writeEntry( QStringLiteral( "Gui" ), QStringLiteral( "/CanvasColorGreenPart" ), myColor.green() );
825-
QgsProject::instance()->writeEntry( QStringLiteral( "Gui" ), QStringLiteral( "/CanvasColorBluePart" ), myColor.blue() );
826-
mMapCanvas->setCanvasColor( myColor );
827-
QgisApp::instance()->mapOverviewCanvas()->setBackgroundColor( myColor );
828-
QgisApp::instance()->mapOverviewCanvas()->refresh();
822+
QColor canvasColor = pbnCanvasColor->color();
823+
QgsProject::instance()->writeEntry( QStringLiteral( "Gui" ), QStringLiteral( "/CanvasColorRedPart" ), canvasColor.red() );
824+
QgsProject::instance()->writeEntry( QStringLiteral( "Gui" ), QStringLiteral( "/CanvasColorGreenPart" ), canvasColor.green() );
825+
QgsProject::instance()->writeEntry( QStringLiteral( "Gui" ), QStringLiteral( "/CanvasColorBluePart" ), canvasColor.blue() );
826+
827+
Q_FOREACH ( QgsMapCanvas *canvas, QgisApp::instance()->mapCanvases() )
828+
{
829+
canvas->setCanvasColor( canvasColor );
830+
canvas->setSelectionColor( selectionColor );
831+
canvas->enableMapTileRendering( mMapTileRenderingCheckBox->isChecked() );
832+
}
833+
QgisApp::instance()->mapOverviewCanvas()->setBackgroundColor( canvasColor );
829834

830835
//save project scales
831836
QStringList myScales;
@@ -1139,7 +1144,12 @@ void QgsProjectProperties::apply()
11391144
//save variables
11401145
QgsProject::instance()->setCustomVariables( mVariableEditor->variablesInActiveScope() );
11411146

1142-
emit refresh();
1147+
//refresh canvases to reflect new properties, eg background color and scale bar after changing display units.
1148+
Q_FOREACH ( QgsMapCanvas *canvas, QgisApp::instance()->mapCanvases() )
1149+
{
1150+
canvas->refresh();
1151+
}
1152+
QgisApp::instance()->mapOverviewCanvas()->refresh();
11431153
}
11441154

11451155
void QgsProjectProperties::showProjectionsTab()

‎src/app/qgsprojectproperties.h

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -162,9 +162,6 @@ class APP_EXPORT QgsProjectProperties : public QgsOptionsDialogBase, private Ui:
162162
//! Signal used to inform listeners that project scale list may have changed
163163
void scalesChanged( const QStringList &scales = QStringList() );
164164

165-
//! let listening canvases know to refresh
166-
void refresh();
167-
168165
private:
169166

170167
//! Formats for displaying coordinates

‎src/gui/qgisinterface.h

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,26 @@ class GUI_EXPORT QgisInterface : public QObject
104104
*/
105105
virtual bool removeCustomActionForLayerType( QAction *action ) = 0;
106106

107+
/**
108+
* Returns a list of all map canvases open in the app.
109+
* @note added in QGIS 3.0
110+
*/
111+
virtual QList< QgsMapCanvas * > mapCanvases() = 0;
112+
113+
/**
114+
* Create a new map canvas with the specified unique \a name.
115+
* @note added in QGIS 3.0
116+
* @see closeMapCanvas()
117+
*/
118+
virtual QgsMapCanvas *createNewMapCanvas( const QString &name ) = 0;
119+
120+
/**
121+
* Closes the additional map canvas with matching \a name.
122+
* @note added in QGIS 3.0
123+
* @see createNewMapCanvas()
124+
*/
125+
virtual void closeMapCanvas( const QString &name ) = 0;
126+
107127
public slots: // TODO: do these functions really need to be slots?
108128

109129
/* Exposed functions */

‎src/gui/qgsmapcanvas.cpp

Lines changed: 56 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1544,7 +1544,6 @@ void QgsMapCanvas::unsetMapTool( QgsMapTool *tool )
15441544
}
15451545
}
15461546

1547-
//! Write property of QColor bgColor.
15481547
void QgsMapCanvas::setCanvasColor( const QColor &color )
15491548
{
15501549
// background of map's pixmap
@@ -1561,7 +1560,7 @@ void QgsMapCanvas::setCanvasColor( const QColor &color )
15611560

15621561
// background of QGraphicsScene
15631562
mScene->setBackgroundBrush( bgBrush );
1564-
} // setBackgroundColor
1563+
}
15651564

15661565
QColor QgsMapCanvas::canvasColor() const
15671566
{
@@ -1858,6 +1857,21 @@ void QgsMapCanvas::readProject( const QDomDocument &doc )
18581857
{
18591858
QDomNode node = nodes.item( 0 );
18601859

1860+
// Search the specific MapCanvas node using the name
1861+
if ( nodes.count() > 1 )
1862+
{
1863+
for ( int i = 0; i < nodes.size(); ++i )
1864+
{
1865+
QDomElement elementNode = nodes.at( i ).toElement();
1866+
1867+
if ( elementNode.hasAttribute( QStringLiteral( "name" ) ) && elementNode.attribute( QStringLiteral( "name" ) ) == objectName() )
1868+
{
1869+
node = nodes.at( i );
1870+
break;
1871+
}
1872+
}
1873+
}
1874+
18611875
QgsMapSettings tmpSettings;
18621876
tmpSettings.readXml( node );
18631877
setDestinationCrs( tmpSettings.destinationCrs() );
@@ -1867,6 +1881,16 @@ void QgsMapCanvas::readProject( const QDomDocument &doc )
18671881
enableMapTileRendering( tmpSettings.testFlag( QgsMapSettings::RenderMapTile ) );
18681882

18691883
clearExtentHistory(); // clear the extent history on project load
1884+
1885+
QDomElement elem = node.toElement();
1886+
if ( elem.hasAttribute( QStringLiteral( "theme" ) ) )
1887+
{
1888+
if ( QgsProject::instance()->mapThemeCollection()->hasMapTheme( elem.attribute( QStringLiteral( "theme" ) ) ) )
1889+
{
1890+
setTheme( elem.attribute( QStringLiteral( "theme" ) ) );
1891+
}
1892+
}
1893+
setAnnotationsVisible( elem.attribute( QStringLiteral( "annotationsVisible" ), QStringLiteral( "1" ) ).toInt() );
18701894
}
18711895
else
18721896
{
@@ -1887,6 +1911,10 @@ void QgsMapCanvas::writeProject( QDomDocument &doc )
18871911
QDomNode qgisNode = nl.item( 0 ); // there should only be one, so zeroth element ok
18881912

18891913
QDomElement mapcanvasNode = doc.createElement( QStringLiteral( "mapcanvas" ) );
1914+
mapcanvasNode.setAttribute( QStringLiteral( "name" ), objectName() );
1915+
if ( !mTheme.isEmpty() )
1916+
mapcanvasNode.setAttribute( QStringLiteral( "theme" ), mTheme );
1917+
mapcanvasNode.setAttribute( QStringLiteral( "annotationsVisible" ), mAnnotationsVisible );
18901918
qgisNode.appendChild( mapcanvasNode );
18911919

18921920
mSettings.writeXml( mapcanvasNode, doc );
@@ -2048,3 +2076,29 @@ void QgsMapCanvas::setSegmentationToleranceType( QgsAbstractGeometry::Segmentati
20482076
{
20492077
mSettings.setSegmentationToleranceType( type );
20502078
}
2079+
2080+
QList<QgsMapCanvasAnnotationItem *> QgsMapCanvas::annotationItems() const
2081+
{
2082+
QList<QgsMapCanvasAnnotationItem *> annotationItemList;
2083+
QList<QGraphicsItem *> itemList = mScene->items();
2084+
QList<QGraphicsItem *>::iterator it = itemList.begin();
2085+
for ( ; it != itemList.end(); ++it )
2086+
{
2087+
QgsMapCanvasAnnotationItem *aItem = dynamic_cast< QgsMapCanvasAnnotationItem *>( *it );
2088+
if ( aItem )
2089+
{
2090+
annotationItemList.push_back( aItem );
2091+
}
2092+
}
2093+
2094+
return annotationItemList;
2095+
}
2096+
2097+
void QgsMapCanvas::setAnnotationsVisible( bool show )
2098+
{
2099+
mAnnotationsVisible = show;
2100+
Q_FOREACH ( QgsMapCanvasAnnotationItem *item, annotationItems() )
2101+
{
2102+
item->setVisible( show );
2103+
}
2104+
}

‎src/gui/qgsmapcanvas.h

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ class QgsMapOverviewCanvas;
6363
class QgsMapTool;
6464
class QgsSnappingUtils;
6565
class QgsRubberBand;
66-
66+
class QgsMapCanvasAnnotationItem;
6767

6868
/** \ingroup gui
6969
* Map canvas is a class for displaying all GIS data types on a canvas.
@@ -468,6 +468,26 @@ class GUI_EXPORT QgsMapCanvas : public QGraphicsView
468468
@param type the segmentation tolerance typename*/
469469
void setSegmentationToleranceType( QgsAbstractGeometry::SegmentationToleranceType type );
470470

471+
/**
472+
* Returns a list of all annotation items in the canvas.
473+
* @note added in QGIS 3.0
474+
*/
475+
QList< QgsMapCanvasAnnotationItem *> annotationItems() const;
476+
477+
/**
478+
* Returns true if annotations are visible within the map canvas.
479+
* @note added in QGIS 3.0
480+
* @see setAnnotationsVisible()
481+
*/
482+
bool annotationsVisible() const { return mAnnotationsVisible; }
483+
484+
/**
485+
* Sets whether annotations are \a visible in the canvas.
486+
* @note added in QGIS 3.0
487+
* @see annotationsVisible()
488+
*/
489+
void setAnnotationsVisible( bool visible );
490+
471491
public slots:
472492

473493
//! Repaints the canvas map
@@ -784,6 +804,8 @@ class GUI_EXPORT QgsMapCanvas : public QGraphicsView
784804

785805
QString mTheme;
786806

807+
bool mAnnotationsVisible = true;
808+
787809
//! Force a resize of the map canvas item
788810
//! @note added in 2.16
789811
void updateMapSize();

‎src/gui/qgsmapcanvasannotationitem.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,9 @@ QgsMapCanvasAnnotationItem::QgsMapCanvasAnnotationItem( QgsAnnotation *annotatio
3333
, mAnnotation( annotation )
3434
{
3535
setFlag( QGraphicsItem::ItemIsSelectable, true );
36+
if ( mapCanvas && !mapCanvas->annotationsVisible() )
37+
setVisible( false );
38+
3639
connect( mAnnotation, &QgsAnnotation::appearanceChanged, this, [this] { update(); } );
3740
connect( mAnnotation, &QgsAnnotation::moved, this, [this] { updatePosition(); } );
3841
connect( mAnnotation, &QgsAnnotation::moved, this, &QgsMapCanvasAnnotationItem::setFeatureForMapPosition );

‎src/ui/qgisapp.ui

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@
5454
<addaction name="mActionDwgImport"/>
5555
<addaction name="separator"/>
5656
<addaction name="mActionSnappingOptions"/>
57+
<addaction name="separator"/>
5758
<addaction name="mActionNewPrintComposer"/>
5859
<addaction name="mActionShowComposerManager"/>
5960
<addaction name="mPrintComposersMenu"/>
@@ -91,6 +92,7 @@
9192
<addaction name="mActionPreviewProtanope"/>
9293
<addaction name="mActionPreviewDeuteranope"/>
9394
</widget>
95+
<addaction name="mActionNewMapCanvas"/>
9496
<addaction name="mActionPan"/>
9597
<addaction name="mActionPanToSelected"/>
9698
<addaction name="mActionZoomIn"/>
@@ -332,6 +334,7 @@
332334
<addaction name="mActionOpenProject"/>
333335
<addaction name="mActionSaveProject"/>
334336
<addaction name="mActionSaveProjectAs"/>
337+
<addaction name="mActionNewMapCanvas"/>
335338
<addaction name="mActionNewPrintComposer"/>
336339
<addaction name="mActionShowComposerManager"/>
337340
</widget>
@@ -640,6 +643,21 @@
640643
<string>Save as &amp;Image...</string>
641644
</property>
642645
</action>
646+
<action name="mActionNewMapCanvas">
647+
<property name="icon">
648+
<iconset resource="../../images/images.qrc">
649+
<normaloff>:/images/themes/default/mActionNewMap.svg</normaloff>:/images/themes/default/mActionNewMap.svg</iconset>
650+
</property>
651+
<property name="text">
652+
<string>New &amp;Map View</string>
653+
</property>
654+
<property name="toolTip">
655+
<string>New Map View</string>
656+
</property>
657+
<property name="shortcut">
658+
<string>Ctrl+M</string>
659+
</property>
660+
</action>
643661
<action name="mActionNewPrintComposer">
644662
<property name="icon">
645663
<iconset resource="../../images/images.qrc">

‎src/ui/qgsmapcanvasdockwidgetbase.ui

Lines changed: 167 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,167 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<ui version="4.0">
3+
<class>QgsMapCanvasDockWidgetBase</class>
4+
<widget class="QgsDockWidget" name="QgsMapCanvasDockWidgetBase">
5+
<property name="geometry">
6+
<rect>
7+
<x>0</x>
8+
<y>0</y>
9+
<width>216</width>
10+
<height>138</height>
11+
</rect>
12+
</property>
13+
<property name="windowTitle">
14+
<string>Map Canvas</string>
15+
</property>
16+
<widget class="QWidget" name="mContents">
17+
<layout class="QVBoxLayout" name="verticalLayout">
18+
<property name="spacing">
19+
<number>0</number>
20+
</property>
21+
<property name="leftMargin">
22+
<number>0</number>
23+
</property>
24+
<property name="topMargin">
25+
<number>0</number>
26+
</property>
27+
<property name="rightMargin">
28+
<number>0</number>
29+
</property>
30+
<property name="bottomMargin">
31+
<number>0</number>
32+
</property>
33+
<item>
34+
<widget class="QToolBar" name="mToolbar">
35+
<property name="iconSize">
36+
<size>
37+
<width>16</width>
38+
<height>16</height>
39+
</size>
40+
</property>
41+
<property name="floatable">
42+
<bool>false</bool>
43+
</property>
44+
<addaction name="mActionSyncView"/>
45+
<addaction name="mActionZoomFullExtent"/>
46+
<addaction name="mActionZoomToSelected"/>
47+
<addaction name="mActionZoomToLayer"/>
48+
</widget>
49+
</item>
50+
<item>
51+
<widget class="QWidget" name="mMainWidget" native="true">
52+
<layout class="QHBoxLayout" name="horizontalLayout">
53+
<property name="spacing">
54+
<number>0</number>
55+
</property>
56+
<property name="leftMargin">
57+
<number>0</number>
58+
</property>
59+
<property name="topMargin">
60+
<number>0</number>
61+
</property>
62+
<property name="rightMargin">
63+
<number>0</number>
64+
</property>
65+
<property name="bottomMargin">
66+
<number>0</number>
67+
</property>
68+
</layout>
69+
</widget>
70+
</item>
71+
</layout>
72+
</widget>
73+
<action name="mActionSetCrs">
74+
<property name="icon">
75+
<iconset resource="../../images/images.qrc">
76+
<normaloff>:/images/themes/default/propertyicons/CRS.svg</normaloff>:/images/themes/default/propertyicons/CRS.svg</iconset>
77+
</property>
78+
<property name="text">
79+
<string>Set Map CRS…</string>
80+
</property>
81+
<property name="toolTip">
82+
<string>Set Map CRS</string>
83+
</property>
84+
</action>
85+
<action name="mActionSyncView">
86+
<property name="checkable">
87+
<bool>true</bool>
88+
</property>
89+
<property name="icon">
90+
<iconset resource="../../images/images.qrc">
91+
<normaloff>:/images/themes/default/mActionLockExtent.svg</normaloff>:/images/themes/default/mActionLockExtent.svg</iconset>
92+
</property>
93+
<property name="text">
94+
<string>Synchronize View</string>
95+
</property>
96+
<property name="toolTip">
97+
<string>Synchronize View Center with Main Map</string>
98+
</property>
99+
</action>
100+
<action name="mActionRename">
101+
<property name="text">
102+
<string>Rename View…</string>
103+
</property>
104+
<property name="toolTip">
105+
<string>Rename View</string>
106+
</property>
107+
</action>
108+
<action name="mActionZoomToSelected">
109+
<property name="icon">
110+
<iconset resource="../../images/images.qrc">
111+
<normaloff>:/images/themes/default/mActionZoomToSelected.svg</normaloff>:/images/themes/default/mActionZoomToSelected.svg</iconset>
112+
</property>
113+
<property name="text">
114+
<string>Zoom to &amp;Selection</string>
115+
</property>
116+
</action>
117+
<action name="mActionZoomToLayer">
118+
<property name="icon">
119+
<iconset resource="../../images/images.qrc">
120+
<normaloff>:/images/themes/default/mActionZoomToLayer.svg</normaloff>:/images/themes/default/mActionZoomToLayer.svg</iconset>
121+
</property>
122+
<property name="text">
123+
<string>Zoom to &amp;Layer</string>
124+
</property>
125+
</action>
126+
<action name="mActionZoomFullExtent">
127+
<property name="icon">
128+
<iconset resource="../../images/images.qrc">
129+
<normaloff>:/images/themes/default/mActionZoomFullExtent.svg</normaloff>:/images/themes/default/mActionZoomFullExtent.svg</iconset>
130+
</property>
131+
<property name="text">
132+
<string>Zoom &amp;Full</string>
133+
</property>
134+
</action>
135+
<action name="mActionShowAnnotations">
136+
<property name="checkable">
137+
<bool>true</bool>
138+
</property>
139+
<property name="text">
140+
<string>Show Annotations</string>
141+
</property>
142+
<property name="toolTip">
143+
<string>Show Annotations</string>
144+
</property>
145+
</action>
146+
<action name="mActionShowCursor">
147+
<property name="checkable">
148+
<bool>true</bool>
149+
</property>
150+
<property name="text">
151+
<string>Show Cursor Position</string>
152+
</property>
153+
</action>
154+
</widget>
155+
<customwidgets>
156+
<customwidget>
157+
<class>QgsDockWidget</class>
158+
<extends>QDockWidget</extends>
159+
<header>qgsdockwidget.h</header>
160+
<container>1</container>
161+
</customwidget>
162+
</customwidgets>
163+
<resources>
164+
<include location="../../images/images.qrc"/>
165+
</resources>
166+
<connections/>
167+
</ui>

‎tests/src/python/test_qgsmapcanvas.py

Lines changed: 36 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,8 @@
1414

1515
import qgis # NOQA
1616

17-
from qgis.core import (QgsCoordinateReferenceSystem,
17+
from qgis.core import (QgsMapSettings,
18+
QgsCoordinateReferenceSystem,
1819
QgsRectangle,
1920
QgsVectorLayer,
2021
QgsFeature,
@@ -23,10 +24,13 @@
2324
QgsFillSymbol,
2425
QgsSingleSymbolRenderer,
2526
QgsMapThemeCollection,
26-
QgsProject)
27+
QgsProject,
28+
QgsApplication)
2729
from qgis.gui import (QgsMapCanvas)
2830

29-
from qgis.PyQt.QtCore import QDir
31+
from qgis.PyQt.QtCore import (Qt,
32+
QDir)
33+
from qgis.PyQt.QtXml import (QDomDocument, QDomElement)
3034
import time
3135
from qgis.testing import start_app, unittest
3236

@@ -337,6 +341,35 @@ def canvasImageCheck(self, name, reference_image, canvas):
337341
print((self.report))
338342
return result
339343

344+
def testSaveMultipleCanvasesToProject(self):
345+
# test saving/restoring canvas state to project with multiple canvases
346+
c1 = QgsMapCanvas()
347+
c1.setObjectName('c1')
348+
c1.setDestinationCrs(QgsCoordinateReferenceSystem('EPSG:3111'))
349+
c1.setRotation(45)
350+
c2 = QgsMapCanvas()
351+
c2.setObjectName('c2')
352+
c2.setDestinationCrs(QgsCoordinateReferenceSystem('EPSG:4326'))
353+
c2.setRotation(65)
354+
355+
doc = QDomDocument("testdoc")
356+
elem = doc.createElement("qgis")
357+
doc.appendChild(elem)
358+
c1.writeProject(doc)
359+
c2.writeProject(doc)
360+
361+
c3 = QgsMapCanvas()
362+
c3.setObjectName('c1')
363+
c4 = QgsMapCanvas()
364+
c4.setObjectName('c2')
365+
c3.readProject(doc)
366+
c4.readProject(doc)
367+
368+
self.assertEqual(c3.mapSettings().destinationCrs().authid(), 'EPSG:3111')
369+
self.assertEqual(c3.rotation(), 45)
370+
self.assertEqual(c4.mapSettings().destinationCrs().authid(), 'EPSG:4326')
371+
self.assertEqual(c4.rotation(), 65)
372+
340373

341374
if __name__ == '__main__':
342375
unittest.main()

0 commit comments

Comments
 (0)
Please sign in to comment.