/*************************************************************************** qgsproject.h Implements persistent project state. ------------------- begin : July 23, 2004 copyright : (C) 2004 by Mark Coletti email : mcoletti at gmail.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 QGSPROJECT_H #define QGSPROJECT_H #include "qgis_core.h" #include "qgis_sip.h" #include "qgis.h" #include #include #include #include #include #include #include #include "qgsunittypes.h" #include "qgssnappingconfig.h" #include "qgsprojectversion.h" #include "qgsexpressioncontextgenerator.h" #include "qgscoordinatereferencesystem.h" #include "qgscoordinatetransformcontext.h" #include "qgsprojectproperty.h" #include "qgsmaplayer.h" #include "qgsmaplayerstore.h" #include "qgsarchive.h" #include "qgsreadwritecontext.h" #include "qgsprojectmetadata.h" class QFileInfo; class QDomDocument; class QDomElement; class QDomNode; class QgsLayerTreeGroup; class QgsLayerTreeRegistryBridge; class QgsMapLayer; class QgsMapThemeCollection; class QgsPathResolver; class QgsProjectBadLayerHandler; class QgsRelationManager; class QgsTolerance; class QgsTransactionGroup; class QgsVectorLayer; class QgsAnnotationManager; class QgsLayoutManager; class QgsLayerTree; class QgsLabelingEngineSettings; class QgsAuxiliaryStorage; /** * \ingroup core * Reads and writes project states. * \note Has two general kinds of state to make persistent. (I.e., to read and write.) First, QGIS proprietary information. Second plug-in information. A singleton since there shall only be one active project at a time; and provides canonical location for plug-ins and main app to find/set properties. */ class CORE_EXPORT QgsProject : public QObject, public QgsExpressionContextGenerator { Q_OBJECT Q_PROPERTY( QStringList nonIdentifiableLayers READ nonIdentifiableLayers WRITE setNonIdentifiableLayers NOTIFY nonIdentifiableLayersChanged ) Q_PROPERTY( QString fileName READ fileName WRITE setFileName NOTIFY fileNameChanged ) Q_PROPERTY( QString homePath READ homePath WRITE setPresetHomePath NOTIFY homePathChanged ) Q_PROPERTY( QgsCoordinateReferenceSystem crs READ crs WRITE setCrs NOTIFY crsChanged ) Q_PROPERTY( QString ellipsoid READ ellipsoid WRITE setEllipsoid NOTIFY ellipsoidChanged ) Q_PROPERTY( QgsMapThemeCollection *mapThemeCollection READ mapThemeCollection NOTIFY mapThemeCollectionChanged ) Q_PROPERTY( QgsSnappingConfig snappingConfig READ snappingConfig WRITE setSnappingConfig NOTIFY snappingConfigChanged ) Q_PROPERTY( QgsRelationManager *relationManager READ relationManager ) Q_PROPERTY( QList avoidIntersectionsLayers READ avoidIntersectionsLayers WRITE setAvoidIntersectionsLayers NOTIFY avoidIntersectionsLayersChanged ) Q_PROPERTY( QgsProjectMetadata metadata READ metadata WRITE setMetadata NOTIFY metadataChanged ) public: //! Returns the QgsProject singleton instance static QgsProject *instance(); /** * Create a new QgsProject. * * Most of the time you want to use QgsProject::instance() instead as many components of QGIS work with the singleton. */ explicit QgsProject( QObject *parent SIP_TRANSFERTHIS = nullptr ); ~QgsProject() override; /** * Sets the project's title. * \param title new title * \since QGIS 2.4 * * \note Since QGIS 3.2 this is just a shortcut to setting the title in the project's metadata(). * * \see title() */ void setTitle( const QString &title ); /** * Returns the project's title. * \see setTitle() * * \note Since QGIS 3.2 this is just a shortcut to retrieving the title from the project's metadata(). */ QString title() const; /** * Returns true if the project has been modified since the last write() */ bool isDirty() const; /** * Sets the file name associated with the project. This is the file which contains the project's XML * representation. * \param name project file name * \see fileName() */ void setFileName( const QString &name ); /** * Returns the project's file name. This is the file which contains the project's XML * representation. * \see setFileName() * \see fileInfo() */ QString fileName() const; /** * Returns QFileInfo object for the project's associated file. * \see fileName() * \since QGIS 2.9 */ QFileInfo fileInfo() const; /** * Returns the project's native coordinate reference system. * \since QGIS 3.0 * \see setCrs() * \see ellipsoid() */ QgsCoordinateReferenceSystem crs() const; /** * Sets the project's native coordinate reference system. * \since QGIS 3.0 * \see crs() * \see setEllipsoid() */ void setCrs( const QgsCoordinateReferenceSystem &crs ); /** * Returns a proj string representing the project's ellipsoid setting, e.g., "WGS84". * \see setEllipsoid() * \see crs() * \since QGIS 3.0 */ QString ellipsoid() const; /** * Sets the project's ellipsoid from a proj string representation, e.g., "WGS84". * \see ellipsoid() * \see setCrs() * \since QGIS 3.0 */ void setEllipsoid( const QString &ellipsoid ); /** * Returns a copy of the project's coordinate transform context, which stores various * information regarding which datum transforms should be used when transforming points * from a source to destination coordinate reference system. * * \since QGIS 3.0 * \see setTransformContext() * \see transformContextChanged() */ QgsCoordinateTransformContext transformContext() const; /** * Sets the project's coordinate transform \a context, which stores various * information regarding which datum transforms should be used when transforming points * from a source to destination coordinate reference system. * * \since QGIS 3.0 * \see transformContext() * \see transformContextChanged() */ void setTransformContext( const QgsCoordinateTransformContext &context ); /** * Clear the project - removes all settings and resets it back to an empty, default state. * \since QGIS 2.4 */ void clear(); /** * Reads given project file from the given file. * \param filename name of project file to read * \returns true if project file has been read successfully */ bool read( const QString &filename ); /** * Reads the project from its currently associated file (see fileName() ). * \returns true if project file has been read successfully */ bool read(); /** * Reads the layer described in the associated DOM node. * * \note This method is mainly for use by QgsProjectBadLayerHandler subclasses * that may fix definition of bad layers with the user's help in GUI. Calling * this method with corrected DOM node adds the layer back to the project. * * \param layerNode represents a QgsProject DOM node that encodes a specific layer. */ bool readLayer( const QDomNode &layerNode ); /** * Writes the project to a file. * \param filename destination file * \returns true if project was written successfully * \note calling this implicitly sets the project's filename (see setFileName() ) * \note isDirty() will be set to false if project is successfully written * \since QGIS 3.0 */ bool write( const QString &filename ); /** * Writes the project to its current associated file (see fileName() ). * \returns true if project was written successfully * \note isDirty() will be set to false if project is successfully written */ bool write(); /** * Write a boolean entry to the project file. * * Keys are '/'-delimited entries, implying * a hierarchy of keys and corresponding values * * \note The key string must be valid xml tag names in order to be saved to the file. * \note available in Python bindings as writeEntryBool */ bool writeEntry( const QString &scope, const QString &key, bool value ) SIP_PYNAME( writeEntryBool ); /** * Write a double entry to the project file. * * Keys are '/'-delimited entries, implying * a hierarchy of keys and corresponding values * * \note The key string must be valid xml tag names in order to be saved to the file. * \note available in Python bindings as writeEntryDouble */ bool writeEntry( const QString &scope, const QString &key, double value ) SIP_PYNAME( writeEntryDouble ); /** * Write an integer entry to the project file. * * Keys are '/'-delimited entries, implying * a hierarchy of keys and corresponding values * * \note The key string must be valid xml tag names in order to be saved to the file. */ bool writeEntry( const QString &scope, const QString &key, int value ); /** * Write a string entry to the project file. * * Keys are '/'-delimited entries, implying * a hierarchy of keys and corresponding values * * \note The key string must be valid xml tag names in order to be saved to the file. */ bool writeEntry( const QString &scope, const QString &key, const QString &value ); /** * Write a string list entry to the project file. * * Keys are '/'-delimited entries, implying * a hierarchy of keys and corresponding values * * \note The key string must be valid xml tag names in order to be saved to the file. */ bool writeEntry( const QString &scope, const QString &key, const QStringList &value ); /** * Key value accessors * * keys would be the familiar QgsSettings-like '/' delimited entries, * implying a hierarchy of keys and corresponding values */ QStringList readListEntry( const QString &scope, const QString &key, const QStringList &def = QStringList(), bool *ok = nullptr ) const; QString readEntry( const QString &scope, const QString &key, const QString &def = QString(), bool *ok = nullptr ) const; int readNumEntry( const QString &scope, const QString &key, int def = 0, bool *ok = nullptr ) const; double readDoubleEntry( const QString &scope, const QString &key, double def = 0, bool *ok = nullptr ) const; bool readBoolEntry( const QString &scope, const QString &key, bool def = false, bool *ok = nullptr ) const; //! Remove the given key bool removeEntry( const QString &scope, const QString &key ); /** * Return keys with values -- do not return keys that contain other keys * * \note equivalent to QgsSettings entryList() */ QStringList entryList( const QString &scope, const QString &key ) const; /** * Return keys with keys -- do not return keys that contain only values * * \note equivalent to QgsSettings subkeyList() */ QStringList subkeyList( const QString &scope, const QString &key ) const; /** * Dump out current project properties to stderr */ // TODO Now slightly broken since re-factoring. Won't print out top-level key // and redundantly prints sub-keys. void dumpProperties() const; /** * Return path resolver object with considering whether the project uses absolute * or relative paths and using current project's path. * \since QGIS 3.0 */ QgsPathResolver pathResolver() const; /** * Prepare a filename to save it to the project file. * Creates an absolute or relative path according to the project settings. * Paths written to the project file should be prepared with this method. */ QString writePath( const QString &filename ) const; //! Turn filename read from the project file to an absolute path QString readPath( const QString &filename ) const; //! Return error message from previous read/write QString error() const; /** * Change handler for missing layers. * Deletes old handler and takes ownership of the new one. */ void setBadLayerHandler( QgsProjectBadLayerHandler *handler SIP_TRANSFER ); //! Returns project file path if layer is embedded from other project file. Returns empty string if layer is not embedded QString layerIsEmbedded( const QString &id ) const; /** * Creates a maplayer instance defined in an arbitrary project file. Caller takes ownership * \returns the layer or 0 in case of error * \note not available in Python bindings */ bool createEmbeddedLayer( const QString &layerId, const QString &projectFilePath, QList &brokenNodes, bool saveFlag = true ) SIP_SKIP; /** * Create layer group instance defined in an arbitrary project file. * \since QGIS 2.4 */ QgsLayerTreeGroup *createEmbeddedGroup( const QString &groupName, const QString &projectFilePath, const QStringList &invisibleLayers ); //! Convenience function to set topological editing void setTopologicalEditing( bool enabled ); //! Convenience function to query topological editing status bool topologicalEditing() const; /** * Convenience function to query default distance measurement units for project. * \since QGIS 2.14 * \see setDistanceUnits() * \see areaUnits() */ QgsUnitTypes::DistanceUnit distanceUnits() const; /** * Sets the default distance measurement units for the project. * \since QGIS 3.0 * \see distanceUnits() * \see setAreaUnits() */ void setDistanceUnits( QgsUnitTypes::DistanceUnit unit ); /** * Convenience function to query default area measurement units for project. * \since QGIS 2.14 * \see distanceUnits() */ QgsUnitTypes::AreaUnit areaUnits() const; /** * Sets the default area measurement units for the project. * \since QGIS 3.0 * \see areaUnits() * \see setDistanceUnits() */ void setAreaUnits( QgsUnitTypes::AreaUnit unit ); /** * Returns the project's home path. This will either be a manually set home path * (see presetHomePath()) or the path containing the project file itself. * * This method always returns the absolute path to the project's home. See * presetHomePath() to retrieve any manual project home path override (e.g. * relative home paths). * * \see setPresetHomePath() * \see presetHomePath() * \see homePathChanged() */ QString homePath() const; /** * Returns any manual project home path setting, or an empty string if not set. * * This path may be a relative path. See homePath() to retrieve a path which is always * an absolute path. * * \see homePath() * \see setPresetHomePath() * \see homePathChanged() * * \since QGIS 3.2 */ QString presetHomePath() const; QgsRelationManager *relationManager() const; /** * Returns the project's layout manager, which manages compositions within * the project. * \since QGIS 3.0 * \note not available in Python bindings */ const QgsLayoutManager *layoutManager() const SIP_SKIP; /** * Returns the project's layout manager, which manages compositions within * the project. * \since QGIS 3.0 */ QgsLayoutManager *layoutManager(); /** * Return pointer to the root (invisible) node of the project's layer tree * \since QGIS 2.4 */ QgsLayerTree *layerTreeRoot() const; /** * Return pointer to the helper class that synchronizes map layer registry with layer tree * \since QGIS 2.4 */ QgsLayerTreeRegistryBridge *layerTreeRegistryBridge() const { return mLayerTreeRegistryBridge; } /** * Returns pointer to the project's map theme collection. * \since QGIS 2.12 * \note renamed in QGIS 3.0, formerly QgsVisibilityPresetCollection */ QgsMapThemeCollection *mapThemeCollection(); /** * Returns pointer to the project's annotation manager. * \since QGIS 3.0 */ QgsAnnotationManager *annotationManager(); /** * Returns a const pointer to the project's annotation manager. * \since QGIS 3.0 */ const QgsAnnotationManager *annotationManager() const SIP_SKIP; /** * Set a list of layers which should not be taken into account on map identification */ void setNonIdentifiableLayers( const QList &layers ); /** * Set a list of layers which should not be taken into account on map identification */ void setNonIdentifiableLayers( const QStringList &layerIds ); /** * Get the list of layers which currently should not be taken into account on map identification */ QStringList nonIdentifiableLayers() const; /** * Transactional editing means that on supported datasources (postgres databases) the edit state of * all tables that originate from the same database are synchronized and executed in a server side * transaction. * * \since QGIS 2.16 */ bool autoTransaction() const; /** * Transactional editing means that on supported datasources (postgres databases) the edit state of * all tables that originate from the same database are synchronized and executed in a server side * transaction. * * Make sure that this is only called when all layers are not in edit mode. * * \since QGIS 2.16 */ void setAutoTransaction( bool autoTransaction ); /** * Map of transaction groups * * QPair( providerKey, connString ) -> transactionGroup * * \since QGIS 2.16 * \note Not available in Python bindings */ QMap< QPair< QString, QString>, QgsTransactionGroup *> transactionGroups() SIP_SKIP; /** * Should default values be evaluated on provider side when requested and not when committed. * * \since QGIS 2.16 */ bool evaluateDefaultValues() const; /** * Defines if default values should be evaluated on provider side when requested and not when committed. * * \since QGIS 2.16 */ void setEvaluateDefaultValues( bool evaluateDefaultValues ); QgsExpressionContext createExpressionContext() const override; /** * The snapping configuration for this project. * * \since QGIS 3.0 */ QgsSnappingConfig snappingConfig() const; /** * A list of layers with which intersections should be avoided. * * \since QGIS 3.0 */ QList avoidIntersectionsLayers() const; /** * A list of layers with which intersections should be avoided. * * \since QGIS 3.0 */ void setAvoidIntersectionsLayers( const QList &layers ); /** * A map of custom project variables. * To get all available variables including generated ones * use QgsExpressionContextUtils::projectScope() instead. */ QVariantMap customVariables() const; /** * A map of custom project variables. * Be careful not to set generated variables. */ void setCustomVariables( const QVariantMap &customVariables ); /** * Sets project's global labeling engine settings * \since QGIS 3.0 */ void setLabelingEngineSettings( const QgsLabelingEngineSettings &settings ); /** * Returns project's global labeling engine settings * \since QGIS 3.0 */ const QgsLabelingEngineSettings &labelingEngineSettings() const; // // Functionality from QgsMapLayerRegistry // /** * Returns a pointer to the project's internal layer store. * /since QGIS 3.0 */ QgsMapLayerStore *layerStore(); /** * Returns a pointer to the project's internal layer store. * /since QGIS 3.0 */ SIP_SKIP const QgsMapLayerStore *layerStore() const; //! Returns the number of registered layers. int count() const; /** * Retrieve a pointer to a registered layer by layer ID. * \param layerId ID of layer to retrieve * \returns matching layer, or nullptr if no matching layer found * \see mapLayersByName() * \see mapLayers() */ QgsMapLayer *mapLayer( const QString &layerId ) const; /** * Retrieve a list of matching registered layers by layer name. * \param layerName name of layers to match * \returns list of matching layers * \see mapLayer() * \see mapLayers() */ QList mapLayersByName( const QString &layerName ) const; /** * Returns a map of all registered layers by layer ID. * \see mapLayer() * \see mapLayersByName() * \see layers() */ QMap mapLayers() const; /** * Returns true if the project comes from a zip archive, false otherwise. */ bool isZipped() const; #ifndef SIP_RUN /** * Returns a list of registered map layers with a specified layer type. * * Example: * * QVector vectorLayers = QgsProject::instance()->layers(); * * \note not available in Python bindings * \since QGIS 2.16 * \see mapLayers() */ template QVector layers() const { return mLayerStore->layers(); } #endif /** * \brief * Add a list of layers to the map of loaded layers. * * The layersAdded() and layerWasAdded() signals will always be emitted. * The legendLayersAdded() signal is emitted only if addToLegend is true. * * \param mapLayers A list of layer which should be added to the registry * \param addToLegend If true (by default), the layers will be added to the * legend and to the main canvas. If you have a private * layer you can set this parameter to false to hide it. * \param takeOwnership Ownership will be transferred to the layer registry. * If you specify false here you have take care of deleting * the layers yourself. Not available in Python. * * \returns a list of the map layers that were added * successfully. If a layer is invalid, or already exists in the registry, * it will not be part of the returned QList. * * \note As a side-effect QgsProject is made dirty. * \note takeOwnership is not available in the Python bindings - the registry will always * take ownership * \since QGIS 1.8 * \see addMapLayer() */ QList addMapLayers( const QList &mapLayers SIP_TRANSFER, bool addToLegend = true, bool takeOwnership SIP_PYARGREMOVE = true ); /** * \brief * Add a layer to the map of loaded layers. * * The layersAdded() and layerWasAdded() signals will always be emitted. * The legendLayersAdded() signal is emitted only if addToLegend is true. * If you are adding multiple layers at once, you should use * addMapLayers() instead. * * \param mapLayer A layer to add to the registry * \param addToLegend If true (by default), the layer will be added to the * legend and to the main canvas. If you have a private * layer you can set this parameter to false to hide it. * \param takeOwnership Ownership will be transferred to the layer registry. * If you specify false here you have take care of deleting * the layer yourself. Not available in Python. * * \returns nullptr if unable to add layer, otherwise pointer to newly added layer * * \see addMapLayers * * \note As a side-effect QgsProject is made dirty. * \note Use addMapLayers if adding more than one layer at a time * \note takeOwnership is not available in the Python bindings - the registry will always * take ownership * \see addMapLayers() */ QgsMapLayer *addMapLayer( QgsMapLayer *mapLayer SIP_TRANSFER, bool addToLegend = true, bool takeOwnership SIP_PYARGREMOVE = true ); /** * \brief * Remove a set of layers from the registry by layer ID. * * The specified layers will be removed from the registry. If the registry has ownership * of any layers these layers will also be deleted. * * \param layerIds list of IDs of the layers to remove * * \note As a side-effect the QgsProject instance is marked dirty. * \since QGIS 1.8 * \see removeMapLayer() * \see removeAllMapLayers() */ void removeMapLayers( const QStringList &layerIds ); /** * \brief * Remove a set of layers from the registry. * * The specified layers will be removed from the registry. If the registry has ownership * of any layers these layers will also be deleted. * * \param layers A list of layers to remove. Null pointers are ignored. * * \note As a side-effect the QgsProject instance is marked dirty. * \see removeMapLayer() * \see removeAllMapLayers() */ //TODO QGIS 3.0 - add PyName alias to avoid list type conversion error void removeMapLayers( const QList &layers ); /** * \brief * Remove a layer from the registry by layer ID. * * The specified layer will be removed from the registry. If the registry has ownership * of the layer then it will also be deleted. * * \param layerId ID of the layer to remove * * \note As a side-effect the QgsProject instance is marked dirty. * \see removeMapLayers() * \see removeAllMapLayers() */ void removeMapLayer( const QString &layerId ); /** * \brief * Remove a layer from the registry. * * The specified layer will be removed from the registry. If the registry has ownership * of the layer then it will also be deleted. * * \param layer The layer to remove. Null pointers are ignored. * * \note As a side-effect the QgsProject instance is marked dirty. * \see removeMapLayers() * \see removeAllMapLayers() */ void removeMapLayer( QgsMapLayer *layer ); /** * Takes a layer from the registry. If the layer was owned by the project, the * layer will be returned without deleting it. The caller takes ownership of * the layer and is responsible for deleting it. * \see removeMapLayer() * \since QGIS 3.0 */ QgsMapLayer *takeMapLayer( QgsMapLayer *layer ) SIP_TRANSFERBACK; /** * Removes all registered layers. If the registry has ownership * of any layers these layers will also be deleted. * * \note As a side-effect the QgsProject instance is marked dirty. * \note Calling this method will cause the removeAll() signal to * be emitted. * \see removeMapLayer() * \see removeMapLayers() */ void removeAllMapLayers(); /** * Reload all registered layer's provider data caches, synchronising the layer * with any changes in the datasource. * \see QgsMapLayer::reload() */ void reloadAllLayers(); /** * Returns the default CRS for new layers based on the settings and * the current project CRS */ QgsCoordinateReferenceSystem defaultCrsForNewLayers() const; /** * Sets the trust option allowing to indicate if the extent has to be * read from the XML document when data source has no metadata or if the * data provider has to determine it. Moreover, when this option is * activated, primary key unicity is not checked for views and * materialized views with Postgres provider. * * \param trust True to trust the project, false otherwise * * \since QGIS 3.0 */ void setTrustLayerMetadata( bool trust ); /** * Returns true if the trust option is activated, false otherwise. This * option allows indicateing if the extent has to be read from the XML * document when data source has no metadata or if the data provider has * to determine it. Moreover, when this option is activated, primary key * unicity is not checked for views and materialized views with Postgres * provider. * * \since QGIS 3.0 */ bool trustLayerMetadata() const { return mTrustLayerMetadata; } /** * Returns the current const auxiliary storage. * * \since QGIS 3.0 */ const QgsAuxiliaryStorage *auxiliaryStorage() const SIP_SKIP; /** * Returns the current auxiliary storage. * * \since QGIS 3.0 */ QgsAuxiliaryStorage *auxiliaryStorage(); /** * Returns a reference to the project's metadata store. * \since QGIS 3.2 * \see setMetadata() * \see metadataChanged() */ const QgsProjectMetadata &metadata() const; /** * Sets the project's \a metadata store. * \since QGIS 3.2 * \see metadata() * \see metadataChanged() */ void setMetadata( const QgsProjectMetadata &metadata ); signals: //! emitted when project is being read void readProject( const QDomDocument & ); //! emitted when project is being written void writeProject( QDomDocument & ); /** * Emitted, after the basic initialization of a layer from the project * file is done. You can use this signal to read additional information * from the project file. * * \param mapLayer The map layer which is being initialized * \param layerNode The layer node from the project file */ void readMapLayer( QgsMapLayer *mapLayer, const QDomElement &layerNode ); /** * Emitted, when a layer is being saved. You can use this method to save * additional information to the layer. * * \param mapLayer The map layer which is being initialized * \param layerElem The layer element from the project file * \param doc The document */ void writeMapLayer( QgsMapLayer *mapLayer, QDomElement &layerElem, QDomDocument &doc ); //! emitted when the project file has been written and closed void projectSaved(); //! emitted when an old project file is read. void oldProjectVersionWarning( const QString & ); /** * Emitted when a layer from a projects was read. * \param i current layer * \param n number of layers */ void layerLoaded( int i, int n ); //! Emitted when a layer is loaded void loadingLayer( const QString &layerName ); /** * \brief Emitted when loading layers has produced some messages * \param layerName the layer name * \param messages a list of pairs of Qgis::MessageLevel and messages * \since 3.2 */ void loadingLayerMessageReceived( const QString &layerName, const QList &messages ); //! Emitted when the list of layer which are excluded from map identification changes void nonIdentifiableLayersChanged( QStringList nonIdentifiableLayers ); //! Emitted when the file name of the project changes void fileNameChanged(); /** * Emitted when the home path of the project changes. * \see setPresetHomePath() * \see homePath() * \see presetHomePath() */ void homePathChanged(); //! emitted whenever the configuration for snapping has changed void snappingConfigChanged( const QgsSnappingConfig &config ); /** * Emitted whenever the expression variables stored in the project have been changed. * \since QGIS 3.0 */ void customVariablesChanged(); /** * Emitted when the CRS of the project has changed. * * \since QGIS 3.0 */ void crsChanged(); /** * Emitted when the project \a ellipsoid is changed. * * \since QGIS 3.0 * \see setEllipsoid() * \see ellipsoid() */ void ellipsoidChanged( const QString &ellipsoid ); /** * Emitted when the project transformContext() is changed. * * \since QGIS 3.0 * \see transformContext() */ void transformContextChanged(); /** * Emitted when datum transforms stored in the project are not available locally. * \since QGIS 3.0 */ void missingDatumTransforms( const QStringList &missingTransforms ); /** * Emitted whenever a new transaction group has been created or a * transaction group has been removed. * * \since QGIS 3.0 */ void transactionGroupsChanged(); /** * Emitted when the topological editing flag has changed. * * \since QGIS 3.0 */ void topologicalEditingChanged(); /** * Emitted whenever avoidIntersectionsLayers has changed. * * \since QGIS 3.0 */ void avoidIntersectionsLayersChanged(); /** * Emitted when the map theme collection changes. * This only happens when the map theme collection is reset. * Any pointer previously received from mapThemeCollection() * must no longer be used after this signal is emitted. * You must still connect to signals from the map theme collection * if you want to be notified about new map themes being added and * map themes being removed. * * \since QGIS 3.0 */ void mapThemeCollectionChanged(); /** * Emitted when global configuration of the labeling engine changes. * \since QGIS 3.0 */ void labelingEngineSettingsChanged(); /** * Emitted when the project's metadata is changed. * \see setMetadata() * \see metadata() * \since QGIS 3.2 */ void metadataChanged(); // // signals from QgsMapLayerRegistry // /** * Emitted when one or more layers are about to be removed from the registry. * * \param layerIds A list of IDs for the layers which are to be removed. * \see layerWillBeRemoved() * \see layersRemoved() */ void layersWillBeRemoved( const QStringList &layerIds ); /** * Emitted when one or more layers are about to be removed from the registry. * * \param layers A list of layers which are to be removed. * \see layerWillBeRemoved() * \see layersRemoved() */ void layersWillBeRemoved( const QList &layers ); /** * Emitted when a layer is about to be removed from the registry. * * \param layerId The ID of the layer to be removed. * * \note Consider using layersWillBeRemoved() instead * \see layersWillBeRemoved() * \see layerRemoved() */ void layerWillBeRemoved( const QString &layerId ); /** * Emitted when a layer is about to be removed from the registry. * * \param layer The layer to be removed. * * \note Consider using layersWillBeRemoved() instead * \see layersWillBeRemoved() * \see layerRemoved() */ void layerWillBeRemoved( QgsMapLayer *layer ); /** * Emitted after one or more layers were removed from the registry. * * \param layerIds A list of IDs of the layers which were removed. * \see layersWillBeRemoved() */ void layersRemoved( const QStringList &layerIds ); /** * Emitted after a layer was removed from the registry. * * \param layerId The ID of the layer removed. * * \note Consider using layersRemoved() instead * \see layerWillBeRemoved() */ void layerRemoved( const QString &layerId ); /** * Emitted when all layers are removed, before layersWillBeRemoved() and * layerWillBeRemoved() signals are emitted. The layersWillBeRemoved() and * layerWillBeRemoved() signals will still be emitted following this signal. * You can use this signal to do easy (and fast) cleanup. */ //TODO QGIS 3.0 - rename to past tense void removeAll(); /** * Emitted when one or more layers were added to the registry. * This signal is also emitted for layers added to the registry, * but not to the legend. * * \param layers List of layers which have been added. * * \see legendLayersAdded() * \see layerWasAdded() */ void layersAdded( const QList &layers ); /** * Emitted when a layer was added to the registry. * * \note Consider using layersAdded() instead * \see layersAdded() */ void layerWasAdded( QgsMapLayer *layer ); /** * Emitted, when a layer was added to the registry and the legend. * Layers can also be private layers, which are signalled by * layersAdded() and layerWasAdded() but will not be * advertised by this signal. * * \param layers List of QgsMapLayer which were added to the legend. */ void legendLayersAdded( const QList &layers ); /** * Emitted when the project dirty status changes. * * \param dirty True if the project is in a dirty state and has pending unsaved changes. * \since QGIS 3.2 * */ void isDirtyChanged( bool dirty ); public slots: /** * The snapping configuration for this project. * * \since QGIS 3.0 */ void setSnappingConfig( const QgsSnappingConfig &snappingConfig ); // TODO QGIS 4.0 - rename b to dirty /** * Flag the project as dirty (modified). If this flag is set, the user will * be asked to save changes to the project before closing the current project. * * \since QGIS 2.4 * \note promoted to public slot in 2.16 */ void setDirty( bool b = true ); /** * Sets the project's home \a path. If an empty path is specified than the * home path will be automatically determined from the project's file path. * \since QGIS 3.2 * \see presetHomePath() * \see homePath() * \see homePathChanged() */ void setPresetHomePath( const QString &path ); private slots: void onMapLayersAdded( const QList &layers ); void onMapLayersRemoved( const QList &layers ); void cleanTransactionGroups( bool force = false ); private: static QgsProject *sProject; /** * Read map layers from project file. * \param doc DOM document to parse * \param brokenNodes a list of DOM nodes corresponding to layers that we were unable to load; this could be * because the layers were removed or re-located after the project was last saved * \returns true if function worked; else is false */ bool _getMapLayers( const QDomDocument &doc, QList &brokenNodes ); /** * Set error message from read/write operation * \note not available in Python bindings */ void setError( const QString &errorMessage ) SIP_SKIP; /** * Clear error message * \note not available in Python bindings */ void clearError() SIP_SKIP; /** * Creates layer and adds it to maplayer registry * \note not available in Python bindings */ bool addLayer( const QDomElement &layerElem, QList &brokenNodes, QgsReadWriteContext &context ) SIP_SKIP; //! \note not available in Python bindings void initializeEmbeddedSubtree( const QString &projectFilePath, QgsLayerTreeGroup *group ) SIP_SKIP; //! \note not available in Python bindings void loadEmbeddedNodes( QgsLayerTreeGroup *group ) SIP_SKIP; //! Read .qgs file bool readProjectFile( const QString &filename ); //! Write .qgs file bool writeProjectFile( const QString &filename ); //! Unzip .qgz file then read embedded .qgs file bool unzip( const QString &filename ); //! Zip project bool zip( const QString &filename ); //! Save auxiliary storage to database bool saveAuxiliaryStorage( const QString &filename = QString() ); std::unique_ptr< QgsMapLayerStore > mLayerStore; QString mErrorMessage; QgsProjectBadLayerHandler *mBadLayerHandler = nullptr; /** * Embedded layers which are defined in other projects. Key: layer id, * value: pair< project file path, save layer yes / no (e.g. if the layer is part of an embedded group, loading/saving is done by the legend) * If the project file path is empty, QgsProject is going to ignore the layer for saving (e.g. because it is part and managed by an embedded group) */ QHash< QString, QPair< QString, bool> > mEmbeddedLayers; QgsSnappingConfig mSnappingConfig; QgsRelationManager *mRelationManager = nullptr; std::unique_ptr mAnnotationManager; std::unique_ptr mLayoutManager; QgsLayerTree *mRootGroup = nullptr; QgsLayerTreeRegistryBridge *mLayerTreeRegistryBridge = nullptr; //! map of transaction group: QPair( providerKey, connString ) -> transactionGroup QMap< QPair< QString, QString>, QgsTransactionGroup *> mTransactionGroups; std::unique_ptr mMapThemeCollection; std::unique_ptr mLabelingEngineSettings; QVariantMap mCustomVariables; std::unique_ptr mArchive; std::unique_ptr mAuxiliaryStorage; QFile mFile; // current physical project file /** * Manual override for project home path - if empty, home path is automatically * created based on file name. */ QString mHomePath; mutable QgsProjectPropertyKey mProperties; // property hierarchy, TODO: this shouldn't be mutable bool mAutoTransaction = false; // transaction grouped editing bool mEvaluateDefaultValues = false; // evaluate default values immediately QgsCoordinateReferenceSystem mCrs; bool mDirty = false; // project has been modified since it has been read or saved int mDirtyBlockCount = 0; bool mTrustLayerMetadata = false; QgsCoordinateTransformContext mTransformContext; QgsProjectMetadata mMetadata; friend class QgsProjectDirtyBlocker; }; /** * Temporarily blocks QgsProject "dirtying" for the lifetime of the object. * * QgsProjectDirtyBlocker supports "stacked" blocking, so two QgsProjectDirtyBlockers created * for the same project will both need to be destroyed before the project can be dirtied again. * * Note that QgsProjectDirtyBlocker only blocks calls which set the project as dirty - calls * which set the project as clean are not blocked. * * Python scripts should not use QgsProjectDirtyBlocker directly. Instead, use QgsProject.blockDirtying() * \code{.py} * project = QgsProject.instance() * with QgsProject.blockDirtying(project): * # do something * \endcode * * \see QgsProject::setDirty() * * \ingroup core * \since QGIS 3.2 */ class CORE_EXPORT QgsProjectDirtyBlocker { public: /** * Constructor for QgsProjectDirtyBlocker. * * This will block dirtying the specified \a project for the lifetime of this object. */ QgsProjectDirtyBlocker( QgsProject *project ) : mProject( project ) { mProject->mDirtyBlockCount++; } ~QgsProjectDirtyBlocker() { mProject->mDirtyBlockCount--; } private: QgsProject *mProject = nullptr; }; /** * Return the version string found in the given DOM document \returns the version string or an empty string if none found \note not available in Python bindings. */ CORE_EXPORT QgsProjectVersion getVersion( QDomDocument const &doc ) SIP_SKIP; #endif