From d8300987e84ccb2e442a18e0551c400db4794082 Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Mon, 2 Sep 2019 10:11:59 +1000 Subject: [PATCH] [FEATURE][API] New class QgsBookmarkManager Attached to QgsProject, this provides a stable, supported method of managing project bookmarks (vs the old undocumented, not stable approach of directly manipulating project keys) --- .../auto_generated/qgsbookmarkmanager.sip.in | 224 +++++++++++++++++ python/core/auto_generated/qgsproject.sip.in | 9 + python/core/core_auto.sip | 1 + src/core/CMakeLists.txt | 2 + src/core/qgsbookmarkmanager.cpp | 226 ++++++++++++++++++ src/core/qgsbookmarkmanager.h | 219 +++++++++++++++++ src/core/qgsproject.cpp | 17 ++ src/core/qgsproject.h | 18 ++ tests/src/python/test_python_repr.py | 10 +- 9 files changed, 725 insertions(+), 1 deletion(-) create mode 100644 python/core/auto_generated/qgsbookmarkmanager.sip.in create mode 100644 src/core/qgsbookmarkmanager.cpp create mode 100644 src/core/qgsbookmarkmanager.h diff --git a/python/core/auto_generated/qgsbookmarkmanager.sip.in b/python/core/auto_generated/qgsbookmarkmanager.sip.in new file mode 100644 index 00000000000..e4c8fa1f386 --- /dev/null +++ b/python/core/auto_generated/qgsbookmarkmanager.sip.in @@ -0,0 +1,224 @@ +/************************************************************************ + * This file has been generated automatically from * + * * + * src/core/qgsbookmarkmanager.h * + * * + * Do not edit manually ! Edit header and run scripts/sipify.pl again * + ************************************************************************/ + + + + +class QgsBookmark +{ +%Docstring + +Represents a spatial bookmark, with a name, CRS and extent. + +QgsBookmark objects are typically used alongside the QgsBookmarkManager class, +which handles storage of a set of bookmarks. + +.. versionadded:: 3.10 +%End + +%TypeHeaderCode +#include "qgsbookmarkmanager.h" +%End + public: + + QgsBookmark(); +%Docstring +Default constructor, creates an empty bookmark. +%End + + QString id() const; +%Docstring +Returns the bookmark's unique ID. + +.. seealso:: :py:func:`setId` +%End + + void setId( const QString &id ); +%Docstring +Sets the bookmark's unique ``id``. + +.. seealso:: :py:func:`id` +%End + + QString name() const; +%Docstring +Returns the bookmark's name, which is a user-visible string identifying +the bookmark. + +.. seealso:: :py:func:`setName` +%End + + void setName( const QString &name ); +%Docstring +Sets the bookmark's ``name``, which is a user-visible string identifying +the bookmark. + +.. seealso:: :py:func:`name` +%End + + QgsReferencedRectangle extent() const; +%Docstring +Returns the bookmark's spatial extent. + +.. seealso:: :py:func:`setExtent` +%End + + void setExtent( const QgsReferencedRectangle &extent ); +%Docstring +Sets the bookmark's spatial ``extent``. + +.. seealso:: :py:func:`extent` +%End + + static QgsBookmark fromXml( const QDomElement &element, const QDomDocument &doc ); +%Docstring +Creates a bookmark using the properties from a DOM ``element``. + +.. seealso:: :py:func:`writeXml` +%End + + QDomElement writeXml( QDomDocument &doc ) const; +%Docstring +Returns a DOM element representing the bookmark's properties. + +.. seealso:: :py:func:`fromXml` +%End + + SIP_PYOBJECT __repr__(); +%MethodCode + QString str = QStringLiteral( "" ).arg( sipCpp->name(), sipCpp->extent().asWktCoordinates(), sipCpp->extent().crs().authid() ); + sipRes = PyUnicode_FromString( str.toUtf8().constData() ); +%End + +}; + +class QgsBookmarkManager : QObject +{ +%Docstring + +Manages storage of a set of bookmarks. + +QgsBookmarkManager handles the storage, serializing and deserializing +of geographic bookmarks. Usually this class is not constructed directly, but +rather accessed through a QgsProject via :py:func:`QgsProject.bookmarkManager()` + +.. versionadded:: 3.10 +%End + +%TypeHeaderCode +#include "qgsbookmarkmanager.h" +%End + public: + + explicit QgsBookmarkManager( QgsProject *project /TransferThis/ = 0 ); +%Docstring +Constructor for QgsBookmarkManager, with the specified ``parent`` project. +%End + + ~QgsBookmarkManager(); + + QString addBookmark( const QgsBookmark &bookmark, bool *ok /Out/ = 0 ); +%Docstring +Adds a ``bookmark`` to the manager. + +:param bookmark: the bookmark to add + +:return: - The bookmark's ID (or newly generated ID, if no ID was originally set and one was automatically generated) + - ok: will be set to ``True`` if the bookmark was successfully added, or ``False`` if the bookmark could not be added (eg as a result of a duplicate bookmark ID). + + +.. seealso:: :py:func:`removeBookmark` + +.. seealso:: :py:func:`bookmarkAdded` +%End + + bool removeBookmark( const QString &id ); +%Docstring +Removes the bookmark with matching ``id`` from the manager. + +Returns ``True`` if the removal was successful, or ``False`` if the removal failed (eg as a result +of removing a bookmark which is not contained in the manager). + +.. seealso:: :py:func:`addBookmark` + +.. seealso:: :py:func:`bookmarkRemoved` + +.. seealso:: :py:func:`bookmarkAboutToBeRemoved` + +.. seealso:: :py:func:`clear` +%End + + void clear(); +%Docstring +Removes and deletes all bookmarks from the manager. + +.. seealso:: :py:func:`removeBookmark` +%End + + QList< QgsBookmark > bookmarks() const; +%Docstring +Returns a list of all bookmarks contained in the manager. +%End + + QgsBookmark bookmarkById( const QString &id ) const; +%Docstring +Returns the bookmark with a matching ``id``, or an empty bookmark if no matching bookmarks +were found. +%End + + bool readXml( const QDomElement &element, const QDomDocument &doc ); +%Docstring +Reads the manager's state from a DOM element, restoring all bookmarks +present in the XML document. + +.. seealso:: :py:func:`writeXml` +%End + + QDomElement writeXml( QDomDocument &doc ) const; +%Docstring +Returns a DOM element representing the state of the manager. + +.. seealso:: :py:func:`readXml` +%End + + signals: + + void bookmarkAboutToBeAdded( const QString &id ); +%Docstring +Emitted when a bookmark is about to be added to the manager +%End + + void bookmarkAdded( const QString &id ); +%Docstring +Emitted when a bookmark has been added to the manager +%End + + void bookmarkRemoved( const QString &id ); +%Docstring +Emitted when a bookmark was removed from the manager +%End + + void bookmarkAboutToBeRemoved( const QString &id ); +%Docstring +Emitted when a bookmark is about to be removed from the manager +%End + + void bookmarkRenamed( const QgsBookmark &bookmark, const QString &newName ); +%Docstring +Emitted when a bookmark is renamed +%End + +}; + +/************************************************************************ + * This file has been generated automatically from * + * * + * src/core/qgsbookmarkmanager.h * + * * + * Do not edit manually ! Edit header and run scripts/sipify.pl again * + ************************************************************************/ diff --git a/python/core/auto_generated/qgsproject.sip.in b/python/core/auto_generated/qgsproject.sip.in index d392c089555..5a650aa259e 100644 --- a/python/core/auto_generated/qgsproject.sip.in +++ b/python/core/auto_generated/qgsproject.sip.in @@ -555,6 +555,15 @@ the project. .. versionadded:: 3.0 %End + + QgsBookmarkManager *bookmarkManager(); +%Docstring +Returns the project's bookmark manager, which manages bookmarks within +the project. + +.. versionadded:: 3.10 +%End + QgsLayerTree *layerTreeRoot() const; %Docstring Returns pointer to the root (invisible) node of the project's layer tree diff --git a/python/core/core_auto.sip b/python/core/core_auto.sip index f9e4d504b33..af7a3453072 100644 --- a/python/core/core_auto.sip +++ b/python/core/core_auto.sip @@ -341,6 +341,7 @@ %Include auto_generated/qgsanimatedicon.sip %Include auto_generated/qgsauxiliarystorage.sip %Include auto_generated/qgsblockingnetworkrequest.sip +%Include auto_generated/qgsbookmarkmanager.sip %Include auto_generated/qgsbrowsermodel.sip %Include auto_generated/qgsbrowserproxymodel.sip %Include auto_generated/qgscoordinatereferencesystem.sip diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index fd2c7431507..58614ca8b54 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -191,6 +191,7 @@ SET(QGIS_CORE_SRCS qgsauxiliarystorage.cpp qgsbearingutils.cpp qgsblockingnetworkrequest.cpp + qgsbookmarkmanager.cpp qgsbrowsermodel.cpp qgsbrowserproxymodel.cpp qgscachedfeatureiterator.cpp @@ -651,6 +652,7 @@ SET(QGIS_CORE_MOC_HDRS qgsanimatedicon.h qgsauxiliarystorage.h qgsblockingnetworkrequest.h + qgsbookmarkmanager.h qgsbrowsermodel.h qgsbrowserproxymodel.h qgscoordinatereferencesystem.h diff --git a/src/core/qgsbookmarkmanager.cpp b/src/core/qgsbookmarkmanager.cpp new file mode 100644 index 00000000000..a8daa2574e6 --- /dev/null +++ b/src/core/qgsbookmarkmanager.cpp @@ -0,0 +1,226 @@ +/*************************************************************************** + qgsbookmarkmanager.cpp + -------------------- + Date : September 2019 + Copyright : (C) 2019 Nyall Dawson + Email : nyall dot dawson at gmail dot com + *************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#include "qgsbookmarkmanager.h" +#include "qgsproject.h" +#include + +// +// QgsBookMark +// + +QString QgsBookmark::id() const +{ + return mId; +} + +void QgsBookmark::setId( const QString &id ) +{ + mId = id; +} + +QgsBookmark QgsBookmark::fromXml( const QDomElement &element, const QDomDocument & ) +{ + QgsBookmark b; + b.setId( element.attribute( QStringLiteral( "id" ) ) ); + b.setName( element.attribute( QStringLiteral( "name" ) ) ); + const QgsRectangle e = QgsRectangle::fromWkt( element.attribute( QStringLiteral( "extent" ) ) ); + QgsCoordinateReferenceSystem crs; + crs.readXml( element ); + b.setExtent( QgsReferencedRectangle( e, crs ) ); + return b; +} + +QDomElement QgsBookmark::writeXml( QDomDocument &doc ) const +{ + QDomElement bookmarkElem = doc.createElement( QStringLiteral( "Bookmark" ) ); + bookmarkElem.setAttribute( QStringLiteral( "id" ), mId ); + bookmarkElem.setAttribute( QStringLiteral( "name" ), mName ); + bookmarkElem.setAttribute( QStringLiteral( "extent" ), mExtent.asWktPolygon() ); + mExtent.crs().writeXml( bookmarkElem, doc ); + return bookmarkElem; +} + +QString QgsBookmark::name() const +{ + return mName; +} + +void QgsBookmark::setName( const QString &name ) +{ + mName = name; +} + +QgsReferencedRectangle QgsBookmark::extent() const +{ + return mExtent; +} + +void QgsBookmark::setExtent( const QgsReferencedRectangle &extent ) +{ + mExtent = extent; +} + + +QgsBookmarkManager::QgsBookmarkManager( QgsProject *project ) + : QObject( project ) + , mProject( project ) +{ + +} + +QgsBookmarkManager::~QgsBookmarkManager() +{ + clear(); +} + +QString QgsBookmarkManager::addBookmark( const QgsBookmark &b, bool *ok ) +{ + if ( ok ) + *ok = false; + + QgsBookmark bookmark = b; + if ( bookmark.id().isEmpty() ) + bookmark.setId( QUuid::createUuid().toString() ); + else + { + // check for duplicate ID + for ( const QgsBookmark &b : qgis::as_const( mBookmarks ) ) + { + if ( b.id() == bookmark.id() ) + { + return QString(); + } + } + } + + if ( ok ) + *ok = true; + + emit bookmarkAboutToBeAdded( bookmark.id() ); + mBookmarks << bookmark; + emit bookmarkAdded( bookmark.id() ); + mProject->setDirty( true ); + return bookmark.id(); +} + +bool QgsBookmarkManager::removeBookmark( const QString &id ) +{ + if ( id.isEmpty() ) + return false; + + int pos = -1; + int i = 0; + for ( const QgsBookmark &b : qgis::as_const( mBookmarks ) ) + { + if ( b.id() == id ) + { + pos = i; + break; + } + i++; + } + + if ( pos < 0 ) + return false; + + emit bookmarkAboutToBeRemoved( id ); + mBookmarks.removeAt( pos ); + emit bookmarkRemoved( id ); + mProject->setDirty( true ); + return true; +} + +void QgsBookmarkManager::clear() +{ + const QList< QgsBookmark > bookmarks = mBookmarks; + for ( const QgsBookmark &b : bookmarks ) + { + removeBookmark( b.id() ); + } +} + +QList QgsBookmarkManager::bookmarks() const +{ + return mBookmarks; +} + +QgsBookmark QgsBookmarkManager::bookmarkById( const QString &id ) const +{ + for ( const QgsBookmark &b : mBookmarks ) + { + if ( b.id() == id ) + return b; + } + return QgsBookmark(); +} + +bool QgsBookmarkManager::readXml( const QDomElement &element, const QDomDocument &doc ) +{ + clear(); + + QDomElement bookmarksElem = element; + if ( element.tagName() != QStringLiteral( "Bookmarks" ) ) + { + bookmarksElem = element.firstChildElement( QStringLiteral( "Bookmarks" ) ); + } + bool result = true; + if ( bookmarksElem.isNull() ) + { + // handle legacy projects + const int count = QgsProject::instance()->readNumEntry( QStringLiteral( "Bookmarks" ), QStringLiteral( "/count" ) ); + for ( int i = 0; i < count; ++i ) + { + const double minX = QgsProject::instance()->readDoubleEntry( QStringLiteral( "Bookmarks" ), QStringLiteral( "/Row-%1/MinX" ).arg( i ) ); + const double minY = QgsProject::instance()->readDoubleEntry( QStringLiteral( "Bookmarks" ), QStringLiteral( "/Row-%1/MinY" ).arg( i ) ); + const double maxX = QgsProject::instance()->readDoubleEntry( QStringLiteral( "Bookmarks" ), QStringLiteral( "/Row-%1/MaxX" ).arg( i ) ); + const double maxY = QgsProject::instance()->readDoubleEntry( QStringLiteral( "Bookmarks" ), QStringLiteral( "/Row-%1/MaxY" ).arg( i ) ); + const long srid = QgsProject::instance()->readNumEntry( QStringLiteral( "Bookmarks" ), QStringLiteral( "/Row-%1/SRID" ).arg( i ) ); + QgsBookmark b; + b.setId( QStringLiteral( "bookmark_%1" ).arg( i ) ); + b.setName( QgsProject::instance()->readEntry( QStringLiteral( "Bookmarks" ), QStringLiteral( "/Row-%1/Name" ).arg( i ) ) ); + b.setExtent( QgsReferencedRectangle( QgsRectangle( minX, minY, maxX, maxY ), QgsCoordinateReferenceSystem::fromSrsId( srid ) ) ); + + bool added = false; + addBookmark( b, &added ); + result = added && result; + } + return result; + } + + //restore each + QDomNodeList bookmarkNodes = element.elementsByTagName( QStringLiteral( "Bookmark" ) ); + for ( int i = 0; i < bookmarkNodes.size(); ++i ) + { + QgsBookmark b = QgsBookmark::fromXml( bookmarkNodes.at( i ).toElement(), doc ); + bool added = false; + addBookmark( b, &added ); + result = added && result; + } + + return result; +} + +QDomElement QgsBookmarkManager::writeXml( QDomDocument &doc ) const +{ + QDomElement bookmarksElem = doc.createElement( QStringLiteral( "Bookmarks" ) ); + + for ( const QgsBookmark &b : mBookmarks ) + { + QDomElement bookmarkElem = b.writeXml( doc ); + bookmarksElem.appendChild( bookmarkElem ); + } + return bookmarksElem; +} diff --git a/src/core/qgsbookmarkmanager.h b/src/core/qgsbookmarkmanager.h new file mode 100644 index 00000000000..61a1db4966c --- /dev/null +++ b/src/core/qgsbookmarkmanager.h @@ -0,0 +1,219 @@ +/*************************************************************************** + qgsbookmarkmanager.h + ------------------ + Date : Septemeber 2019 + Copyright : (C) 2019 Nyall Dawson + Email : nyall dot dawson at gmail dot com + *************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#ifndef QGSBOOKMARKMANAGER_H +#define QGSBOOKMARKMANAGER_H + +#include "qgis_core.h" +#include "qgis_sip.h" +#include "qgsreferencedgeometry.h" +#include + +class QgsProject; + +/** + * \ingroup core + * \class QgsBookmark + * + * \brief Represents a spatial bookmark, with a name, CRS and extent. + * + * QgsBookmark objects are typically used alongside the QgsBookmarkManager class, + * which handles storage of a set of bookmarks. + * + * \since QGIS 3.10 + */ +class CORE_EXPORT QgsBookmark +{ + + public: + + /** + * Default constructor, creates an empty bookmark. + */ + QgsBookmark() = default; + + /** + * Returns the bookmark's unique ID. + * \see setId() + */ + QString id() const; + + /** + * Sets the bookmark's unique \a id. + * \see id() + */ + void setId( const QString &id ); + + /** + * Returns the bookmark's name, which is a user-visible string identifying + * the bookmark. + * \see setName() + */ + QString name() const; + + /** + * Sets the bookmark's \a name, which is a user-visible string identifying + * the bookmark. + * \see name() + */ + void setName( const QString &name ); + + /** + * Returns the bookmark's spatial extent. + * \see setExtent() + */ + QgsReferencedRectangle extent() const; + + /** + * Sets the bookmark's spatial \a extent. + * \see extent() + */ + void setExtent( const QgsReferencedRectangle &extent ); + + /** + * Creates a bookmark using the properties from a DOM \a element. + * \see writeXml() + */ + static QgsBookmark fromXml( const QDomElement &element, const QDomDocument &doc ); + + /** + * Returns a DOM element representing the bookmark's properties. + * \see fromXml() + */ + QDomElement writeXml( QDomDocument &doc ) const; + +#ifdef SIP_RUN + SIP_PYOBJECT __repr__(); + % MethodCode + QString str = QStringLiteral( "" ).arg( sipCpp->name(), sipCpp->extent().asWktCoordinates(), sipCpp->extent().crs().authid() ); + sipRes = PyUnicode_FromString( str.toUtf8().constData() ); + % End +#endif + + private: + + QString mId; + QString mName; + QgsReferencedRectangle mExtent; + +}; + +/** + * \ingroup core + * \class QgsBookmarkManager + * + * \brief Manages storage of a set of bookmarks. + * + * QgsBookmarkManager handles the storage, serializing and deserializing + * of geographic bookmarks. Usually this class is not constructed directly, but + * rather accessed through a QgsProject via QgsProject::bookmarkManager(). + * + * \since QGIS 3.10 + */ +class CORE_EXPORT QgsBookmarkManager : public QObject +{ + Q_OBJECT + + public: + + /** + * Constructor for QgsBookmarkManager, with the specified \a parent project. + */ + explicit QgsBookmarkManager( QgsProject *project SIP_TRANSFERTHIS = nullptr ); + + ~QgsBookmarkManager() override; + + /** + * Adds a \a bookmark to the manager. + * + * \param bookmark the bookmark to add + * \param ok if specified, will be set to TRUE if the bookmark was successfully added, or + * FALSE if the bookmark could not be added (eg as a result of a duplicate bookmark ID). + * + * \returns The bookmark's ID (or newly generated ID, if no ID was originally set and one was automatically generated) + * + * \see removeBookmark() + * \see bookmarkAdded() + */ + QString addBookmark( const QgsBookmark &bookmark, bool *ok SIP_OUT = nullptr ); + + /** + * Removes the bookmark with matching \a id from the manager. + * + * Returns TRUE if the removal was successful, or FALSE if the removal failed (eg as a result + * of removing a bookmark which is not contained in the manager). + * + * \see addBookmark() + * \see bookmarkRemoved() + * \see bookmarkAboutToBeRemoved() + * \see clear() + */ + bool removeBookmark( const QString &id ); + + /** + * Removes and deletes all bookmarks from the manager. + * \see removeBookmark() + */ + void clear(); + + /** + * Returns a list of all bookmarks contained in the manager. + */ + QList< QgsBookmark > bookmarks() const; + + /** + * Returns the bookmark with a matching \a id, or an empty bookmark if no matching bookmarks + * were found. + */ + QgsBookmark bookmarkById( const QString &id ) const; + + /** + * Reads the manager's state from a DOM element, restoring all bookmarks + * present in the XML document. + * \see writeXml() + */ + bool readXml( const QDomElement &element, const QDomDocument &doc ); + + /** + * Returns a DOM element representing the state of the manager. + * \see readXml() + */ + QDomElement writeXml( QDomDocument &doc ) const; + + signals: + + //! Emitted when a bookmark is about to be added to the manager + void bookmarkAboutToBeAdded( const QString &id ); + + //! Emitted when a bookmark has been added to the manager + void bookmarkAdded( const QString &id ); + + //! Emitted when a bookmark was removed from the manager + void bookmarkRemoved( const QString &id ); + + //! Emitted when a bookmark is about to be removed from the manager + void bookmarkAboutToBeRemoved( const QString &id ); + + //! Emitted when a bookmark is renamed + void bookmarkRenamed( const QgsBookmark &bookmark, const QString &newName ); + + private: + + QgsProject *mProject = nullptr; + QList< QgsBookmark > mBookmarks; + +}; + +#endif // QGSBOOKMARKMANAGER_H diff --git a/src/core/qgsproject.cpp b/src/core/qgsproject.cpp index 39c76d27aa5..f0be29fca0e 100644 --- a/src/core/qgsproject.cpp +++ b/src/core/qgsproject.cpp @@ -49,6 +49,7 @@ #include "qgsmaplayerlistutils.h" #include "qgsmeshlayer.h" #include "qgslayoutmanager.h" +#include "qgsbookmarkmanager.h" #include "qgsmaplayerstore.h" #include "qgsziputils.h" #include "qgsauxiliarystorage.h" @@ -358,6 +359,7 @@ QgsProject::QgsProject( QObject *parent ) , mRelationManager( new QgsRelationManager( this ) ) , mAnnotationManager( new QgsAnnotationManager( this ) ) , mLayoutManager( new QgsLayoutManager( this ) ) + , mBookmarkManager( new QgsBookmarkManager( this ) ) , mRootGroup( new QgsLayerTree ) , mLabelingEngineSettings( new QgsLabelingEngineSettings ) , mArchive( new QgsProjectArchive() ) @@ -724,6 +726,7 @@ void QgsProject::clear() mRelationManager->clear(); mAnnotationManager->clear(); mLayoutManager->clear(); + mBookmarkManager->clear(); mSnappingConfig.reset(); emit snappingConfigChanged( mSnappingConfig ); emit topologicalEditingChanged(); @@ -1393,6 +1396,7 @@ bool QgsProject::readProjectFile( const QString &filename, QgsProject::ReadFlags mAnnotationManager->readXml( doc->documentElement(), context ); mLayoutManager->readXml( doc->documentElement(), *doc ); + mBookmarkManager->readXml( doc->documentElement(), *doc ); // reassign change dependencies now that all layers are loaded QMap existingMaps = mapLayers(); @@ -1938,6 +1942,9 @@ bool QgsProject::writeProjectFile( const QString &filename ) QDomElement layoutElem = mLayoutManager->writeXml( *doc ); qgisNode.appendChild( layoutElem ); + QDomElement bookmarkElem = mBookmarkManager->writeXml( *doc ); + qgisNode.appendChild( bookmarkElem ); + // now wrap it up and ship it to the project file doc->normalize(); // XXX I'm not entirely sure what this does @@ -2597,6 +2604,16 @@ QgsLayoutManager *QgsProject::layoutManager() return mLayoutManager.get(); } +const QgsBookmarkManager *QgsProject::bookmarkManager() const +{ + return mBookmarkManager; +} + +QgsBookmarkManager *QgsProject::bookmarkManager() +{ + return mBookmarkManager; +} + QgsLayerTree *QgsProject::layerTreeRoot() const { return mRootGroup; diff --git a/src/core/qgsproject.h b/src/core/qgsproject.h index d1cad800cf8..e18334d47bc 100644 --- a/src/core/qgsproject.h +++ b/src/core/qgsproject.h @@ -70,6 +70,7 @@ class QgsLayerTree; class QgsLabelingEngineSettings; class QgsAuxiliaryStorage; class QgsMapLayer; +class QgsBookmarkManager; /** * \ingroup core @@ -545,6 +546,21 @@ class CORE_EXPORT QgsProject : public QObject, public QgsExpressionContextGenera */ QgsLayoutManager *layoutManager(); + /** + * Returns the project's bookmark manager, which manages bookmarks within + * the project. + * \note not available in Python bindings + * \since QGIS 3.10 + */ + const QgsBookmarkManager *bookmarkManager() const SIP_SKIP; + + /** + * Returns the project's bookmark manager, which manages bookmarks within + * the project. + * \since QGIS 3.10 + */ + QgsBookmarkManager *bookmarkManager(); + /** * Returns pointer to the root (invisible) node of the project's layer tree * \since QGIS 2.4 @@ -1600,6 +1616,8 @@ class CORE_EXPORT QgsProject : public QObject, public QgsExpressionContextGenera std::unique_ptr mAnnotationManager; std::unique_ptr mLayoutManager; + QgsBookmarkManager *mBookmarkManager = nullptr; + QgsLayerTree *mRootGroup = nullptr; QgsLayerTreeRegistryBridge *mLayerTreeRegistryBridge = nullptr; diff --git a/tests/src/python/test_python_repr.py b/tests/src/python/test_python_repr.py index 0a1a4e569ce..2d0e03999b3 100644 --- a/tests/src/python/test_python_repr.py +++ b/tests/src/python/test_python_repr.py @@ -17,7 +17,7 @@ from qgis.testing import unittest, start_app from qgis.core import QgsGeometry, QgsPoint, QgsPointXY, QgsCircle, QgsCircularString, QgsCompoundCurve,\ QgsCurvePolygon, QgsEllipse, QgsLineString, QgsMultiCurve, QgsRectangle, QgsExpression, QgsField, QgsError,\ QgsMimeDataUtils, QgsVector, QgsVector3D, QgsVectorLayer, QgsReferencedPointXY, QgsReferencedRectangle,\ - QgsCoordinateReferenceSystem, QgsCoordinateTransform, QgsProject, QgsClassificationRange + QgsCoordinateReferenceSystem, QgsCoordinateTransform, QgsProject, QgsClassificationRange, QgsBookmark start_app() @@ -192,6 +192,14 @@ class TestPython__repr__(unittest.TestCase): QgsProject.instance().setFileName('/home/test/my_project.qgs') self.assertEqual(QgsProject.instance().__repr__(), "") + def testQgsBookmark(self): + b = QgsBookmark() + self.assertEqual(b.__repr__(), "") + b.setName('test bookmark') + self.assertEqual(b.__repr__(), "") + b.setExtent(QgsReferencedRectangle(QgsRectangle(1, 2, 3, 4), QgsCoordinateReferenceSystem('EPSG:3111'))) + self.assertEqual(b.__repr__(), "") + if __name__ == "__main__": unittest.main()