80#include <QApplication>
85#include <QTemporaryFile>
88#include <QStandardPaths>
90#include <QRegularExpression>
112 QStringList keyTokens = QStringList( scope );
113 keyTokens += key.split(
'/', Qt::SkipEmptyParts );
116 keyTokens.push_front( QStringLiteral(
"properties" ) );
119 for (
int i = 0; i < keyTokens.size(); ++i )
121 const QString keyToken = keyTokens.at( i );
125 const thread_local QRegularExpression sInvalidRegexp = QRegularExpression( QStringLiteral(
"([^:A-Z_a-z\\x{C0}-\\x{D6}\\x{D8}-\\x{F6}\\x{F8}-\\x{2FF}\\x{370}-\\x{37D}\\x{37F}-\\x{1FFF}\\x{200C}-\\x{200D}\\x{2070}-\\x{218F}\\x{2C00}-\\x{2FEF}\\x{3001}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFFD}\\-\\.0-9\\x{B7}\\x{0300}-\\x{036F}\\x{203F}-\\x{2040}]|^[^:A-Z_a-z\\x{C0}-\\x{D6}\\x{D8}-\\x{F6}\\x{F8}-\\x{2FF}\\x{370}-\\x{37D}\\x{37F}-\\x{1FFF}\\x{200C}-\\x{200D}\\x{2070}-\\x{218F}\\x{2C00}-\\x{2FEF}\\x{3001}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFFD}])" ) );
126 if ( keyToken.contains( sInvalidRegexp ) )
128 const QString errorString = QObject::tr(
"Entry token invalid : '%1'. The token will not be saved to file." ).arg( keyToken );
156 while ( !keySequence.isEmpty() )
160 if ( keySequence.first() == currentProperty->
name() )
163 keySequence.pop_front();
165 if ( 1 == keySequence.count() )
168 return currentProperty->
find( keySequence.front() );
170 else if ( keySequence.isEmpty() )
175 return currentProperty;
177 else if ( ( nextProperty = currentProperty->
find( keySequence.first() ) ) )
179 if ( nextProperty->
isKey() )
183 else if ( nextProperty->
isValue() && 1 == keySequence.count() )
189 return currentProperty;
227 const QVariant &value,
228 bool &propertiesModified )
237 propertiesModified =
false;
238 while ( ! keySequence.isEmpty() )
242 if ( keySequence.first() == currentProperty->
name() )
245 keySequence.pop_front();
249 if ( 1 == keySequence.count() )
252 if ( !property || property->value() != value )
254 currentProperty->
setValue( keySequence.front(), value );
255 propertiesModified =
true;
258 return currentProperty;
262 else if ( keySequence.isEmpty() )
264 if ( currentProperty->
value() != value )
267 propertiesModified =
true;
270 return currentProperty;
272 else if ( ( nextProperty = currentProperty->
find( keySequence.first() ) ) )
276 if ( currentProperty )
287 if ( ( newPropertyKey = currentProperty->
addKey( keySequence.first() ) ) )
289 currentProperty = newPropertyKey;
321 while ( ! keySequence.isEmpty() )
325 if ( keySequence.first() == currentProperty->
name() )
328 keySequence.pop_front();
332 if ( 1 == keySequence.count() )
334 currentProperty->
removeKey( keySequence.front() );
339 else if ( keySequence.isEmpty() )
341 previousQgsPropertyKey->
removeKey( currentProperty->
name() );
343 else if ( ( nextProperty = currentProperty->
find( keySequence.first() ) ) )
345 previousQgsPropertyKey = currentProperty;
348 if ( currentProperty )
372 , mCapabilities( capabilities )
375 , mSnappingConfig( this )
393 mProperties.
setName( QStringLiteral(
"properties" ) );
396 mMainAnnotationLayer->setParent(
this );
410 this, [
this](
const QStringList &
layers ) { mProjectScope.reset(); emit layersWillBeRemoved( layers ); } );
412 this, [
this](
const QList<QgsMapLayer *> &
layers ) { mProjectScope.reset(); emit layersWillBeRemoved( layers ); } );
414 this, [
this](
const QString & layer ) { mProjectScope.reset(); emit layerWillBeRemoved( layer ); } );
416 this, [
this](
QgsMapLayer * layer ) { mProjectScope.reset(); emit layerWillBeRemoved( layer ); } );
418 [
this](
const QStringList &
layers ) { mProjectScope.reset(); emit layersRemoved( layers ); } );
420 [
this](
const QString & layer ) { mProjectScope.reset(); emit layerRemoved( layer ); } );
422 [
this]() { mProjectScope.reset(); emit removeAll(); } );
424 [
this](
const QList< QgsMapLayer * > &
layers ) { mProjectScope.reset(); emit layersAdded( layers ); } );
426 [
this](
QgsMapLayer * layer ) { mProjectScope.reset(); emit layerWasAdded( layer ); } );
434 [
this](
const QList<QgsMapLayer *> &
layers )
436 for ( const auto &layer : layers )
438 disconnect( layer, &QgsMapLayer::dataSourceChanged, mRelationManager, &QgsRelationManager::updateRelationsStatus );
443 [
this](
const QList<QgsMapLayer *> &layers )
445 for ( const auto &layer : layers )
447 connect( layer, &QgsMapLayer::dataSourceChanged, mRelationManager, &QgsRelationManager::updateRelationsStatus );
456 mStyleSettings->combinedStyleModel()->addDefaultStyle();
462 mIsBeingDeleted =
true;
465 releaseHandlesToProjectArchive();
466 delete mBadLayerHandler;
467 delete mRelationManager;
468 delete mLayerTreeRegistryBridge;
470 if (
this == sProject )
501 mProjectScope.reset();
512 return mMetadata.
title();
521 if ( oldEvaluateDefaultValues != newEvaluateDefaultValues )
524 for (
auto layerIt =
layers.constBegin(); layerIt !=
layers.constEnd(); ++layerIt )
526 if (
QgsVectorLayer *vl = qobject_cast<QgsVectorLayer *>( layerIt.value() ) )
527 if ( vl->dataProvider() )
534 if ( oldTrustLayerMetadata != newTrustLayerMetadata )
537 for (
auto layerIt =
layers.constBegin(); layerIt !=
layers.constEnd(); ++layerIt )
539 if (
QgsVectorLayer *vl = qobject_cast<QgsVectorLayer *>( layerIt.value() ) )
541 vl->setReadExtentFromXml( newTrustLayerMetadata );
546 if ( mFlags !=
flags )
561 newFlags &= ~(
static_cast< int >( flag ) );
576 return mSaveUserFull;
583 return mSaveDateTime;
604 if ( dirty && mDirtyBlockCount > 0 )
610 if ( mDirty == dirty )
621 if ( path == mHomePath )
625 mCachedHomePath.clear();
626 mProjectScope.reset();
637 const QList<QgsAttributeEditorElement *> elements = parent->
children();
645 translationContext->
registerTranslation( QStringLiteral(
"project:layers:%1:formcontainers" ).arg( layerId ), container->
name() );
647 if ( !container->
children().empty() )
662 translationContext->
registerTranslation( QStringLiteral(
"project:layers:%1" ).arg( layer->layerId() ), layer->name() );
671 for (
const QgsField &field : fields )
674 if ( field.alias().isEmpty() )
675 fieldName = field.name();
677 fieldName = field.alias();
679 translationContext->
registerTranslation( QStringLiteral(
"project:layers:%1:fieldaliases" ).arg( vlayer->
id() ), fieldName );
681 if ( field.editorWidgetSetup().type() == QLatin1String(
"ValueRelation" ) )
683 translationContext->
registerTranslation( QStringLiteral(
"project:layers:%1:fields:%2:valuerelationvalue" ).arg( vlayer->
id(), field.name() ), field.editorWidgetSetup().config().value( QStringLiteral(
"Value" ) ).toString() );
694 const QList<QgsLayerTreeGroup *> groupLayers = mRootGroup->
findGroups();
697 translationContext->
registerTranslation( QStringLiteral(
"project:layergroups" ), groupLayer->name() );
701 const QList<QgsRelation> &relations = mRelationManager->
relations().values();
704 translationContext->
registerTranslation( QStringLiteral(
"project:relations" ), relation.name() );
712 mDataDefinedServerProperties = properties;
719 return mDataDefinedServerProperties;
726 switch ( mTransactionMode )
747 switch ( mTransactionMode )
754 commitErrors.append( tr(
"Trying to commit changes without a layer specified. This only works if the transaction mode is buffered" ) );
763 return mEditBufferGroup.
commitChanges( commitErrors, stopEditing );
773 switch ( mTransactionMode )
780 rollbackErrors.append( tr(
"Trying to roll back changes without a layer specified. This only works if the transaction mode is buffered" ) );
783 bool success = vectorLayer->
rollBack( stopEditing );
789 return mEditBufferGroup.
rollBack( rollbackErrors, stopEditing );
799 if ( name == mFile.fileName() )
802 const QString oldHomePath =
homePath();
804 mFile.setFileName( name );
805 mCachedHomePath.clear();
806 mProjectScope.reset();
810 const QString newHomePath =
homePath();
811 if ( newHomePath != oldHomePath )
822 return mFile.fileName();
829 mOriginalPath = path;
836 return mOriginalPath;
843 return QFileInfo( mFile );
861 storage->readProjectStorageMetadata( mFile.fileName(),
metadata );
866 return QFileInfo( mFile.fileName() ).lastModified();
877 if ( mFile.fileName().isEmpty() )
880 return QFileInfo( mFile.fileName() ).absolutePath();
891 if ( mFile.fileName().isEmpty() )
894 return QFileInfo( mFile.fileName() ).absoluteFilePath();
905 storage->readProjectStorageMetadata( mFile.fileName(),
metadata );
910 return QFileInfo( mFile.fileName() ).completeBaseName();
918 const bool absolutePaths =
readBoolEntry( QStringLiteral(
"Paths" ), QStringLiteral(
"/Absolute" ),
false );
929 writeEntry( QStringLiteral(
"Paths" ), QStringLiteral(
"/Absolute" ),
true );
932 writeEntry( QStringLiteral(
"Paths" ), QStringLiteral(
"/Absolute" ),
false );
949 return mCrs3D.
isValid() ? mCrs3D : mCrs;
961 writeEntry( QStringLiteral(
"SpatialRefSys" ), QStringLiteral(
"/ProjectionsEnabled" ),
crs.
isValid() ? 1 : 0 );
962 mProjectScope.reset();
976 if ( oldCrs3D != mCrs3D )
980 if ( adjustEllipsoid )
989 if ( !
crs().isValid() )
992 return readEntry( QStringLiteral(
"Measure" ), QStringLiteral(
"/Ellipsoid" ),
geoNone() );
999 if (
ellipsoid ==
readEntry( QStringLiteral(
"Measure" ), QStringLiteral(
"/Ellipsoid" ) ) )
1002 mProjectScope.reset();
1012 switch ( mCrs.
type() )
1015 QgsDebugError( QStringLiteral(
"Project has a vertical CRS set as the horizontal CRS!" ) );
1034 return mVerticalCrs;
1062 *errorMessage = QObject::tr(
"Specified CRS is a %1 CRS, not a Vertical CRS" ).arg(
qgsEnumValueToKey(
crs.
type() ) );
1067 if (
crs != mVerticalCrs )
1072 switch ( mCrs.
type() )
1075 if (
crs != oldVerticalCrs )
1078 *errorMessage = QObject::tr(
"Project CRS is a Compound CRS, specified Vertical CRS will be ignored" );
1084 if (
crs != oldVerticalCrs )
1087 *errorMessage = QObject::tr(
"Project CRS is a Geographic 3D CRS, specified Vertical CRS will be ignored" );
1093 if (
crs != oldVerticalCrs )
1096 *errorMessage = QObject::tr(
"Project CRS is a Geocentric CRS, specified Vertical CRS will be ignored" );
1105 *errorMessage = QObject::tr(
"Project CRS is a Projected 3D CRS, specified Vertical CRS will be ignored" );
1123 res = rebuildCrs3D( errorMessage );
1124 mProjectScope.reset();
1131 if ( mCrs3D != oldCrs3D )
1142 return mTransformContext;
1149 if ( context == mTransformContext )
1152 mTransformContext = context;
1153 mProjectScope.reset();
1156 for (
auto &layer : mLayerStore.get()->mapLayers() )
1158 layer->setTransformContext( context );
1167 ScopedIntIncrementor snapSingleBlocker( &mBlockSnappingUpdates );
1171 if ( !mIsBeingDeleted )
1180 mProjectScope.reset();
1181 mFile.setFileName( QString() );
1184 mSaveUserFull.clear();
1185 mSaveDateTime = QDateTime();
1188 mCachedHomePath.clear();
1192 mCustomVariables.clear();
1198 if ( !mSettings.
value( QStringLiteral(
"projects/anonymize_new_projects" ),
false,
QgsSettings::Core ).toBool() )
1217 mEmbeddedLayers.clear();
1218 mRelationManager->
clear();
1219 mAnnotationManager->clear();
1220 mLayoutManager->clear();
1221 m3DViewsManager->clear();
1222 mBookmarkManager->
clear();
1223 mSensorManager->
clear();
1224 mViewSettings->
reset();
1225 mTimeSettings->
reset();
1226 mElevationProperties->
reset();
1227 mDisplaySettings->
reset();
1228 mGpsSettings->
reset();
1229 mSnappingConfig.
reset();
1237 mLabelingEngineSettings->clear();
1241 releaseHandlesToProjectArchive();
1247 mStyleSettings->
reset();
1251 if ( !mIsBeingDeleted )
1259 writeEntry( QStringLiteral(
"PositionPrecision" ), QStringLiteral(
"/Automatic" ),
true );
1260 writeEntry( QStringLiteral(
"PositionPrecision" ), QStringLiteral(
"/DecimalPlaces" ), 2 );
1262 const bool defaultRelativePaths = mSettings.
value( QStringLiteral(
"/qgis/defaultProjectPathsRelative" ),
true ).toBool();
1265 int red = mSettings.
value( QStringLiteral(
"qgis/default_canvas_color_red" ), 255 ).toInt();
1266 int green = mSettings.
value( QStringLiteral(
"qgis/default_canvas_color_green" ), 255 ).toInt();
1267 int blue = mSettings.
value( QStringLiteral(
"qgis/default_canvas_color_blue" ), 255 ).toInt();
1270 red = mSettings.
value( QStringLiteral(
"qgis/default_selection_color_red" ), 255 ).toInt();
1271 green = mSettings.
value( QStringLiteral(
"qgis/default_selection_color_green" ), 255 ).toInt();
1272 blue = mSettings.
value( QStringLiteral(
"qgis/default_selection_color_blue" ), 0 ).toInt();
1273 const int alpha = mSettings.
value( QStringLiteral(
"qgis/default_selection_color_alpha" ), 255 ).toInt();
1279 mRootGroup->
clear();
1280 if ( mMainAnnotationLayer )
1281 mMainAnnotationLayer->
reset();
1283 snapSingleBlocker.release();
1285 if ( !mBlockSnappingUpdates )
1290 if ( !mBlockChangeSignalsDuringClear )
1302 topQgsPropertyKey.
dump();
1335 const QDomElement propertiesElem = doc.documentElement().firstChildElement( QStringLiteral(
"properties" ) );
1337 if ( propertiesElem.isNull() )
1342 const QDomNodeList scopes = propertiesElem.childNodes();
1344 if ( propertiesElem.firstChild().isNull() )
1346 QgsDebugError( QStringLiteral(
"empty ``properties'' XML tag ... bailing" ) );
1350 if ( ! project_properties.
readXml( propertiesElem ) )
1352 QgsDebugError( QStringLiteral(
"Project_properties.readXml() failed" ) );
1366 const QDomElement ddElem = doc.documentElement().firstChildElement( QStringLiteral(
"dataDefinedServerProperties" ) );
1367 if ( !ddElem.isNull() )
1369 if ( !ddServerProperties.
readXml( ddElem, dataDefinedServerPropertyDefinitions ) )
1371 QgsDebugError( QStringLiteral(
"dataDefinedServerProperties.readXml() failed" ) );
1374 return ddServerProperties;
1381static void _getTitle(
const QDomDocument &doc, QString &title )
1383 const QDomElement titleNode = doc.documentElement().firstChildElement( QStringLiteral(
"title" ) );
1387 if ( titleNode.isNull() )
1393 if ( !titleNode.hasChildNodes() )
1399 const QDomNode titleTextNode = titleNode.firstChild();
1401 if ( !titleTextNode.isText() )
1407 const QDomText titleText = titleTextNode.toText();
1409 title = titleText.data();
1413static void readProjectFileMetadata(
const QDomDocument &doc, QString &lastUser, QString &lastUserFull, QDateTime &lastSaveDateTime )
1415 const QDomNodeList nl = doc.elementsByTagName( QStringLiteral(
"qgis" ) );
1419 QgsDebugError( QStringLiteral(
"unable to find qgis element" ) );
1423 const QDomNode qgisNode = nl.item( 0 );
1425 const QDomElement qgisElement = qgisNode.toElement();
1426 lastUser = qgisElement.attribute( QStringLiteral(
"saveUser" ), QString() );
1427 lastUserFull = qgisElement.attribute( QStringLiteral(
"saveUserFull" ), QString() );
1428 lastSaveDateTime = QDateTime::fromString( qgisElement.attribute( QStringLiteral(
"saveDateTime" ), QString() ), Qt::ISODate );
1433 const QDomNodeList nl = doc.elementsByTagName( QStringLiteral(
"qgis" ) );
1437 QgsDebugError( QStringLiteral(
" unable to find qgis element in project file" ) );
1441 const QDomNode qgisNode = nl.item( 0 );
1443 const QDomElement qgisElement = qgisNode.toElement();
1444 QgsProjectVersion projectVersion( qgisElement.attribute( QStringLiteral(
"version" ) ) );
1445 return projectVersion;
1452 return mSnappingConfig;
1471 if ( mAvoidIntersectionsMode == mode )
1474 mAvoidIntersectionsMode = mode;
1508void QgsProject::preloadProviders(
const QVector<QDomNode> ¶llelLayerNodes,
1510 QMap<QString, QgsDataProvider *> &loadedProviders,
1512 int totalProviderCount )
1517 QMap<QString, LayerToLoad> layersToLoad;
1519 for (
const QDomNode &node : parallelLayerNodes )
1523 const QDomElement layerElement = node.toElement();
1525 layerToLoad.
layerId = layerElement.namedItem( QStringLiteral(
"id" ) ).toElement().text();
1526 layerToLoad.
provider = layerElement.namedItem( QStringLiteral(
"provider" ) ).toElement().text();
1527 layerToLoad.
dataSource = layerElement.namedItem( QStringLiteral(
"datasource" ) ).toElement().text();
1538 layersToLoad.insert( layerToLoad.
layerId, layerToLoad );
1541 while ( !layersToLoad.isEmpty() )
1543 const QList<LayerToLoad> layersToAttemptInParallel = layersToLoad.values();
1544 QString layerToAttemptInMainThread;
1546 QHash<QString, QgsRunnableProviderCreator *> runnables;
1547 QThreadPool threadPool;
1550 for (
const LayerToLoad &lay : layersToAttemptInParallel )
1553 runnables.insert( lay.layerId, run );
1559 layersToLoad.remove( layId );
1562 Q_ASSERT( finishedRun );
1564 std::unique_ptr<QgsDataProvider> provider( finishedRun->
dataProvider() );
1565 Q_ASSERT( provider && provider->isValid() );
1567 loadedProviders.insert( layId, provider.release() );
1572 if ( layerToAttemptInMainThread.isEmpty() )
1573 layerToAttemptInMainThread = layId;
1577 if ( i == parallelLayerNodes.count() || !isValid )
1580 threadPool.start( run );
1584 threadPool.waitForDone();
1586 qDeleteAll( runnables );
1589 auto it = layersToLoad.find( layerToAttemptInMainThread );
1590 if ( it != layersToLoad.end() )
1592 std::unique_ptr<QgsDataProvider> provider;
1602 if ( provider && provider->isValid() )
1607 layersToLoad.erase( it );
1610 loadedProviders.insert( layerId, provider.release() );
1618void QgsProject::releaseHandlesToProjectArchive()
1623bool QgsProject::rebuildCrs3D( QString *error )
1630 else if ( !mVerticalCrs.
isValid() )
1636 switch ( mCrs.
type() )
1677bool QgsProject::_getMapLayers(
const QDomDocument &doc, QList<QDomNode> &brokenNodes,
Qgis::ProjectReadFlags flags )
1684 QDomElement layerElement = doc.documentElement().firstChildElement( QStringLiteral(
"projectlayers" ) ).firstChildElement( QStringLiteral(
"maplayer" ) );
1688 if ( layerElement.isNull() )
1698 bool returnStatus =
true;
1701 while ( ! layerElement.isNull() )
1704 layerElement = layerElement.nextSiblingElement( QStringLiteral(
"maplayer" ) );
1710 if ( depSorter.hasCycle() )
1714 if ( depSorter.hasMissingDependency() )
1715 returnStatus =
false;
1719 const QVector<QDomNode> sortedLayerNodes = depSorter.sortedLayerNodes();
1720 const int totalLayerCount = sortedLayerNodes.count();
1722 QVector<QDomNode> parallelLoading;
1723 QMap<QString, QgsDataProvider *> loadedProviders;
1727 profile.switchTask( tr(
"Load providers in parallel" ) );
1728 for (
const QDomNode &node : sortedLayerNodes )
1730 const QDomElement element = node.toElement();
1731 if ( element.attribute( QStringLiteral(
"embedded" ) ) != QLatin1String(
"1" ) )
1733 const QString layerId = node.namedItem( QStringLiteral(
"id" ) ).toElement().text();
1734 if ( !depSorter.isLayerDependent( layerId ) )
1736 const QDomNode mnl = element.namedItem( QStringLiteral(
"provider" ) );
1737 const QDomElement mne = mnl.toElement();
1738 const QString provider = mne.text();
1742 parallelLoading.append( node );
1751 if ( !parallelLoading.isEmpty() )
1752 preloadProviders( parallelLoading, context, loadedProviders, projectFlagsToLayerReadFlags(
flags, mFlags ), sortedLayerNodes.count() );
1755 int i = loadedProviders.count();
1756 for (
const QDomNode &node : std::as_const( sortedLayerNodes ) )
1758 const QDomElement element = node.toElement();
1759 const QString name =
translate( QStringLiteral(
"project:layers:%1" ).arg( node.namedItem( QStringLiteral(
"id" ) ).toElement().text() ), node.namedItem( QStringLiteral(
"layername" ) ).toElement().text() );
1760 if ( !name.isNull() )
1761 emit
loadingLayer( tr(
"Loading layer %1" ).arg( name ) );
1763 profile.switchTask( name );
1764 if ( element.attribute( QStringLiteral(
"embedded" ) ) == QLatin1String(
"1" ) )
1766 createEmbeddedLayer( element.attribute( QStringLiteral(
"id" ) ),
readPath( element.attribute( QStringLiteral(
"project" ) ) ), brokenNodes,
true,
flags );
1774 QString layerId = element.namedItem( QStringLiteral(
"id" ) ).toElement().text();
1776 if ( !addLayer( element, brokenNodes, context,
flags, loadedProviders.take( layerId ) ) )
1778 returnStatus =
false;
1781 if ( !messages.isEmpty() )
1790 return returnStatus;
1793bool QgsProject::addLayer(
const QDomElement &layerElem,
1794 QList<QDomNode> &brokenNodes,
1801 const QString type = layerElem.attribute( QStringLiteral(
"type" ) );
1803 std::unique_ptr<QgsMapLayer>
mapLayer;
1811 QgsDebugError( QStringLiteral(
"Unknown layer type \"%1\"" ).arg( type ) );
1815 switch ( layerType )
1818 mapLayer = std::make_unique<QgsVectorLayer>();
1822 mapLayer = std::make_unique<QgsRasterLayer>();
1826 mapLayer = std::make_unique<QgsMeshLayer>();
1830 mapLayer = std::make_unique<QgsVectorTileLayer>();
1834 mapLayer = std::make_unique<QgsPointCloudLayer>();
1838 mapLayer = std::make_unique<QgsTiledSceneLayer>();
1843 const QString
typeName = layerElem.attribute( QStringLiteral(
"name" ) );
1851 mapLayer = std::make_unique<QgsAnnotationLayer>( QString(), options );
1858 mapLayer = std::make_unique<QgsGroupLayer>( QString(), options );
1865 QgsDebugError( QStringLiteral(
"Unable to create layer" ) );
1873 const QString layerId { layerElem.namedItem( QStringLiteral(
"id" ) ).toElement().text() };
1874 Q_ASSERT( ! layerId.isEmpty() );
1880 profile.switchTask( tr(
"Load layer source" ) );
1887 if ( vl->dataProvider() )
1894 profile.switchTask( tr(
"Add layer to project" ) );
1895 QList<QgsMapLayer *> newLayers;
1907 vLayer->joinBuffer()->resolveReferences(
this );
1916 brokenNodes.push_back( layerElem );
1919 const bool wasEditable = layerElem.attribute( QStringLiteral(
"editable" ), QStringLiteral(
"0" ) ).toInt();
1931 if ( ! layerWasStored )
1936 return layerIsValid;
1943 mFile.setFileName( filename );
1944 mCachedHomePath.clear();
1945 mProjectScope.reset();
1954 const QString filename = mFile.fileName();
1959 QTemporaryFile inDevice;
1960 if ( !inDevice.open() )
1962 setError( tr(
"Unable to open %1" ).arg( inDevice.fileName() ) );
1968 if ( !storage->readProject( filename, &inDevice, context ) )
1970 QString err = tr(
"Unable to open %1" ).arg( filename );
1971 QList<QgsReadWriteContext::ReadWriteMessage> messages = context.
takeMessages();
1972 if ( !messages.isEmpty() )
1973 err += QStringLiteral(
"\n\n" ) + messages.last().message();
1977 returnValue = unzip( inDevice.fileName(),
flags );
1983 returnValue = unzip( mFile.fileName(),
flags );
1988 const QFileInfo finfo( mFile.fileName() );
1989 const QString attachmentsZip = finfo.absoluteDir().absoluteFilePath( QStringLiteral(
"%1_attachments.zip" ).arg( finfo.completeBaseName() ) );
1990 if ( QFile( attachmentsZip ).exists() )
1992 std::unique_ptr<QgsArchive> archive(
new QgsArchive() );
1993 if ( archive->unzip( attachmentsZip ) )
1995 releaseHandlesToProjectArchive();
1996 mArchive = std::move( archive );
1999 returnValue = readProjectFile( mFile.fileName(),
flags );
2005 mFile.setFileName( filename );
2006 mCachedHomePath.clear();
2007 mProjectScope.reset();
2012 mTranslator.reset(
nullptr );
2024 ScopedIntIncrementor snapSignalBlock( &mBlockSnappingUpdates );
2026 QFile projectFile( filename );
2034 if ( QFile( QStringLiteral(
"%1/%2.qm" ).arg( QFileInfo( projectFile.fileName() ).absolutePath(), localeFileName ) ).exists() )
2036 mTranslator.reset(
new QTranslator() );
2037 ( void )mTranslator->load( localeFileName, QFileInfo( projectFile.fileName() ).absolutePath() );
2040 profile.switchTask( tr(
"Reading project file" ) );
2041 std::unique_ptr<QDomDocument> doc(
new QDomDocument( QStringLiteral(
"qgis" ) ) );
2043 if ( !projectFile.open( QIODevice::ReadOnly | QIODevice::Text ) )
2045 projectFile.close();
2047 setError( tr(
"Unable to open %1" ).arg( projectFile.fileName() ) );
2052 QTextStream textStream( &projectFile );
2053#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
2054 textStream.setCodec(
"UTF-8" );
2056 QString projectString = textStream.readAll();
2057 projectFile.close();
2059 for (
int i = 0; i < 32; i++ )
2061 if ( i == 9 || i == 10 || i == 13 )
2065 projectString.replace( QChar( i ), QStringLiteral(
"%1%2%1" ).arg(
FONTMARKER_CHR_FIX, QString::number( i ) ) );
2071 if ( !doc->setContent( projectString, &errorMsg, &line, &column ) )
2073 const QString errorString = tr(
"Project file read error in file %1: %2 at line %3 column %4" )
2074 .arg( projectFile.fileName(), errorMsg ).arg( line ).arg( column );
2076 setError( errorString );
2081 projectFile.close();
2089 profile.switchTask( tr(
"Updating project file" ) );
2090 if ( thisVersion > fileVersion )
2092 const bool isOlderMajorVersion = fileVersion.
majorVersion() < thisVersion.majorVersion();
2094 if ( isOlderMajorVersion )
2097 "version of qgis (saved in " + fileVersion.
text() +
2099 "). Problems may occur." );
2110 projectFile.updateRevision( thisVersion );
2112 else if ( fileVersion > thisVersion )
2115 "version of qgis (saved in " + fileVersion.
text() +
2117 "). Problems may occur." );
2123 profile.switchTask( tr(
"Creating auxiliary storage" ) );
2124 const QString
fileName = mFile.fileName();
2131 std::unique_ptr<QgsAuxiliaryStorage> aStorage = std::move( mAuxiliaryStorage );
2132 std::unique_ptr<QgsArchive> archive = std::move( mArchive );
2136 mBlockChangeSignalsDuringClear =
true;
2138 mBlockChangeSignalsDuringClear =
false;
2143 releaseHandlesToProjectArchive();
2145 mAuxiliaryStorage = std::move( aStorage );
2146 mArchive = std::move( archive );
2149 mCachedHomePath.clear();
2150 mProjectScope.reset();
2151 mSaveVersion = fileVersion;
2154 profile.switchTask( tr(
"Reading properties" ) );
2163 dump_( mProperties );
2168 _getTitle( *doc, oldTitle );
2170 readProjectFileMetadata( *doc, mSaveUser, mSaveUserFull, mSaveDateTime );
2172 const QDomNodeList homePathNl = doc->elementsByTagName( QStringLiteral(
"homePath" ) );
2173 if ( homePathNl.count() > 0 )
2175 const QDomElement homePathElement = homePathNl.at( 0 ).toElement();
2176 const QString
homePath = homePathElement.attribute( QStringLiteral(
"path" ) );
2186 readNumEntry( QStringLiteral(
"Gui" ), QStringLiteral(
"/CanvasColorGreenPart" ), 255 ),
2187 readNumEntry( QStringLiteral(
"Gui" ), QStringLiteral(
"/CanvasColorBluePart" ), 255 ) );
2190 readNumEntry( QStringLiteral(
"Gui" ), QStringLiteral(
"/SelectionColorGreenPart" ), 255 ),
2191 readNumEntry( QStringLiteral(
"Gui" ), QStringLiteral(
"/SelectionColorBluePart" ), 255 ),
2192 readNumEntry( QStringLiteral(
"Gui" ), QStringLiteral(
"/SelectionColorAlphaPart" ), 255 ) );
2196 const QString distanceUnitString =
readEntry( QStringLiteral(
"Measurement" ), QStringLiteral(
"/DistanceUnits" ), QString() );
2197 if ( !distanceUnitString.isEmpty() )
2200 const QString areaUnitString =
readEntry( QStringLiteral(
"Measurement" ), QStringLiteral(
"/AreaUnits" ), QString() );
2201 if ( !areaUnitString.isEmpty() )
2210 if (
readNumEntry( QStringLiteral(
"SpatialRefSys" ), QStringLiteral(
"/ProjectionsEnabled" ), 0 ) )
2213 const QDomNode srsNode = doc->documentElement().namedItem( QStringLiteral(
"projectCrs" ) );
2214 if ( !srsNode.isNull() )
2216 projectCrs.
readXml( srsNode );
2221 const QString projCrsString =
readEntry( QStringLiteral(
"SpatialRefSys" ), QStringLiteral(
"/ProjectCRSProj4String" ) );
2222 const long currentCRS =
readNumEntry( QStringLiteral(
"SpatialRefSys" ), QStringLiteral(
"/ProjectCRSID" ), -1 );
2223 const QString authid =
readEntry( QStringLiteral(
"SpatialRefSys" ), QStringLiteral(
"/ProjectCrs" ) );
2226 const bool isUserAuthId = authid.startsWith( QLatin1String(
"USER:" ), Qt::CaseInsensitive );
2227 if ( !authid.isEmpty() && !isUserAuthId )
2231 if ( !projectCrs.
isValid() && currentCRS >= 0 )
2237 if ( !projCrsString.isEmpty() && ( authid.isEmpty() || isUserAuthId ) && ( !projectCrs.
isValid() || projectCrs.
toProj() != projCrsString ) )
2254 const QDomNode verticalCrsNode = doc->documentElement().namedItem( QStringLiteral(
"verticalCrs" ) );
2255 if ( !verticalCrsNode.isNull() )
2263 QStringList datumErrors;
2264 if ( !mTransformContext.
readXml( doc->documentElement(), context, datumErrors ) && !datumErrors.empty() )
2271 const QDomNode elevationShadingNode = doc->documentElement().namedItem( QStringLiteral(
"elevation-shading-renderer" ) );
2272 if ( !elevationShadingNode.isNull() )
2274 mElevationShadingRenderer.
readXml( elevationShadingNode.toElement(), context );
2281 const QStringList variableNames =
readListEntry( QStringLiteral(
"Variables" ), QStringLiteral(
"/variableNames" ) );
2282 const QStringList variableValues =
readListEntry( QStringLiteral(
"Variables" ), QStringLiteral(
"/variableValues" ) );
2284 mCustomVariables.clear();
2285 if ( variableNames.length() == variableValues.length() )
2287 for (
int i = 0; i < variableNames.length(); ++i )
2289 mCustomVariables.insert( variableNames.at( i ), variableValues.at( i ) );
2294 QgsMessageLog::logMessage( tr(
"Project Variables Invalid" ), tr(
"The project contains invalid variable settings." ) );
2302 QDomElement element = doc->documentElement().firstChildElement( QStringLiteral(
"projectMetadata" ) );
2304 if ( !element.isNull() )
2313 if ( mMetadata.
title().isEmpty() && !oldTitle.isEmpty() )
2321 element = doc->documentElement().firstChildElement( QStringLiteral(
"transaction" ) );
2322 if ( !element.isNull() )
2329 element = doc->documentElement().firstChildElement( QStringLiteral(
"autotransaction" ) );
2330 if ( ! element.isNull() )
2332 mTransactionMode =
static_cast<Qgis::TransactionMode>( element.attribute( QStringLiteral(
"active" ), QStringLiteral(
"0" ) ).toInt() );
2337 profile.switchTask( tr(
"Loading layer tree" ) );
2340 QDomElement layerTreeElem = doc->documentElement().firstChildElement( QStringLiteral(
"layer-tree-group" ) );
2341 if ( !layerTreeElem.isNull() )
2353 mLayerTreeRegistryBridge->
setEnabled(
false );
2356 profile.switchTask( tr(
"Reading map layers" ) );
2358 loadProjectFlags( doc.get() );
2360 QList<QDomNode> brokenNodes;
2361 const bool clean = _getMapLayers( *doc, brokenNodes,
flags );
2366 QgsDebugError( QStringLiteral(
"Unable to get map layers from project file." ) );
2368 if ( !brokenNodes.isEmpty() )
2370 QgsDebugError(
"there are " + QString::number( brokenNodes.size() ) +
" broken layers" );
2378 mMainAnnotationLayer->
readLayerXml( doc->documentElement().firstChildElement( QStringLiteral(
"main-annotation-layer" ) ), context );
2382 profile.switchTask( tr(
"Loading embedded layers" ) );
2383 loadEmbeddedNodes( mRootGroup,
flags );
2387 profile.switchTask( tr(
"Resolving layer references" ) );
2388 QMap<QString, QgsMapLayer *>
layers = mLayerStore->mapLayers();
2389 for ( QMap<QString, QgsMapLayer *>::iterator it =
layers.begin(); it !=
layers.end(); ++it )
2391 it.value()->resolveReferences(
this );
2395 mLayerTreeRegistryBridge->
setEnabled(
true );
2398 profile.switchTask( tr(
"Resolving references" ) );
2409 if ( !layerTreeElem.isNull() )
2415 const QDomElement layerTreeCanvasElem = doc->documentElement().firstChildElement( QStringLiteral(
"layer-tree-canvas" ) );
2416 if ( !layerTreeCanvasElem.isNull( ) )
2424 const QStringList requiredLayerIds =
readListEntry( QStringLiteral(
"RequiredLayers" ), QStringLiteral(
"Layers" ) );
2425 for (
const QString &layerId : requiredLayerIds )
2432 const QStringList disabledLayerIds =
readListEntry( QStringLiteral(
"Identify" ), QStringLiteral(
"/disabledLayers" ) );
2433 for (
const QString &layerId : disabledLayerIds )
2446 QString styleName =
readEntry( QStringLiteral(
"DefaultStyles" ), QStringLiteral(
"/Marker" ) );
2447 if ( !styleName.isEmpty() )
2452 styleName =
readEntry( QStringLiteral(
"DefaultStyles" ), QStringLiteral(
"/Line" ) );
2453 if ( !styleName.isEmpty() )
2458 styleName =
readEntry( QStringLiteral(
"DefaultStyles" ), QStringLiteral(
"/Fill" ) );
2459 if ( !styleName.isEmpty() )
2464 styleName =
readEntry( QStringLiteral(
"DefaultStyles" ), QStringLiteral(
"/ColorRamp" ) );
2465 if ( !styleName.isEmpty() )
2475 double opacity = 1.0;
2478 double alpha =
readDoubleEntry( QStringLiteral(
"DefaultStyles" ), QStringLiteral(
"/AlphaInt" ), 255, &ok );
2480 opacity = alpha / 255.0;
2481 double newOpacity =
readDoubleEntry( QStringLiteral(
"DefaultStyles" ), QStringLiteral(
"/Opacity" ), 1.0, &ok );
2483 opacity = newOpacity;
2487 removeEntry( QStringLiteral(
"DefaultStyles" ), QStringLiteral(
"/Marker" ) );
2488 removeEntry( QStringLiteral(
"DefaultStyles" ), QStringLiteral(
"/Line" ) );
2489 removeEntry( QStringLiteral(
"DefaultStyles" ), QStringLiteral(
"/Fill" ) );
2490 removeEntry( QStringLiteral(
"DefaultStyles" ), QStringLiteral(
"/ColorRamp" ) );
2491 removeEntry( QStringLiteral(
"DefaultStyles" ), QStringLiteral(
"/RandomColors" ) );
2492 removeEntry( QStringLiteral(
"DefaultStyles" ), QStringLiteral(
"/AlphaInt" ) );
2493 removeEntry( QStringLiteral(
"DefaultStyles" ), QStringLiteral(
"/Opacity" ) );
2501 profile.switchTask( tr(
"Storing original layer properties" ) );
2507 profile.switchTask( tr(
"Loading map themes" ) );
2510 mMapThemeCollection->readXml( *doc );
2512 profile.switchTask( tr(
"Loading label settings" ) );
2513 mLabelingEngineSettings->readSettingsFromProject(
this );
2515 const QDomElement labelEngineSettingsElement = doc->documentElement().firstChildElement( QStringLiteral(
"labelEngineSettings" ) );
2516 mLabelingEngineSettings->readXml( labelEngineSettingsElement, context );
2518 mLabelingEngineSettings->resolveReferences(
this );
2522 profile.switchTask( tr(
"Loading annotations" ) );
2525 mAnnotationManager->readXml( doc->documentElement(), context );
2529 mAnnotationManager->readXmlAndUpgradeToAnnotationLayerItems( doc->documentElement(), context, mMainAnnotationLayer, mTransformContext );
2533 profile.switchTask( tr(
"Loading layouts" ) );
2534 mLayoutManager->readXml( doc->documentElement(), *doc );
2539 profile.switchTask( tr(
"Loading 3D Views" ) );
2540 m3DViewsManager->readXml( doc->documentElement(), *doc );
2543 profile.switchTask( tr(
"Loading bookmarks" ) );
2544 mBookmarkManager->
readXml( doc->documentElement(), *doc );
2546 profile.switchTask( tr(
"Loading sensors" ) );
2547 mSensorManager->
readXml( doc->documentElement(), *doc );
2550 QMap<QString, QgsMapLayer *> existingMaps =
mapLayers();
2551 for ( QMap<QString, QgsMapLayer *>::iterator it = existingMaps.begin(); it != existingMaps.end(); ++it )
2553 it.value()->setDependencies( it.value()->dependencies() );
2556 profile.switchTask( tr(
"Loading snapping settings" ) );
2560 profile.switchTask( tr(
"Loading view settings" ) );
2563 const QStringList scales =
readListEntry( QStringLiteral(
"Scales" ), QStringLiteral(
"/ScalesList" ) );
2564 QVector<double> res;
2565 for (
const QString &scale : scales )
2567 const QStringList parts = scale.split(
':' );
2568 if ( parts.size() != 2 )
2572 const double denominator = QLocale().toDouble( parts[1], &ok );
2579 const QDomElement viewSettingsElement = doc->documentElement().firstChildElement( QStringLiteral(
"ProjectViewSettings" ) );
2580 if ( !viewSettingsElement.isNull() )
2581 mViewSettings->
readXml( viewSettingsElement, context );
2584 profile.switchTask( tr(
"Loading style properties" ) );
2585 const QDomElement styleSettingsElement = doc->documentElement().firstChildElement( QStringLiteral(
"ProjectStyleSettings" ) );
2586 if ( !styleSettingsElement.isNull() )
2589 mStyleSettings->
readXml( styleSettingsElement, context,
flags );
2593 profile.switchTask( tr(
"Loading temporal settings" ) );
2594 const QDomElement timeSettingsElement = doc->documentElement().firstChildElement( QStringLiteral(
"ProjectTimeSettings" ) );
2595 if ( !timeSettingsElement.isNull() )
2596 mTimeSettings->
readXml( timeSettingsElement, context );
2599 profile.switchTask( tr(
"Loading elevation properties" ) );
2600 const QDomElement elevationPropertiesElement = doc->documentElement().firstChildElement( QStringLiteral(
"ElevationProperties" ) );
2601 if ( !elevationPropertiesElement.isNull() )
2602 mElevationProperties->
readXml( elevationPropertiesElement, context );
2605 profile.switchTask( tr(
"Loading display settings" ) );
2607 const QDomElement displaySettingsElement = doc->documentElement().firstChildElement( QStringLiteral(
"ProjectDisplaySettings" ) );
2608 if ( !displaySettingsElement.isNull() )
2609 mDisplaySettings->
readXml( displaySettingsElement, context );
2612 profile.switchTask( tr(
"Loading GPS settings" ) );
2614 const QDomElement gpsSettingsElement = doc->documentElement().firstChildElement( QStringLiteral(
"ProjectGpsSettings" ) );
2615 if ( !gpsSettingsElement.isNull() )
2616 mGpsSettings->
readXml( gpsSettingsElement, context );
2620 profile.switchTask( tr(
"Updating variables" ) );
2622 profile.switchTask( tr(
"Updating CRS" ) );
2626 if ( mCrs3D != oldCrs3D )
2631 profile.switchTask( tr(
"Reading external settings" ) );
2635 profile.switchTask( tr(
"Updating interface" ) );
2637 snapSignalBlock.release();
2638 if ( !mBlockSnappingUpdates )
2649 QgsDebugMsgLevel( QStringLiteral(
"Project save user: %1" ).arg( mSaveUser ), 2 );
2650 QgsDebugMsgLevel( QStringLiteral(
"Project save user: %1" ).arg( mSaveUserFull ), 2 );
2659 const QString newFileName( QStringLiteral(
"%1/%2.qgs" ).arg( QFileInfo( projectFile.fileName() ).absolutePath(), localeFileName ) );
2674 const QMap<QString, QgsMapLayer *> loadedLayers =
mapLayers();
2675 for (
auto it = loadedLayers.constBegin(); it != loadedLayers.constEnd(); ++it )
2677 if ( it.value()->isValid() && it.value()->customProperty( QStringLiteral(
"_layer_was_editable" ) ).toBool() )
2679 if (
QgsVectorLayer *vl = qobject_cast< QgsVectorLayer * >( it.value() ) )
2681 it.value()->removeCustomProperty( QStringLiteral(
"_layer_was_editable" ) );
2693 const auto constChildren = group->
children();
2699 if ( childGroup->
customProperty( QStringLiteral(
"embedded" ) ).toInt() )
2702 const QString projectPath =
readPath( childGroup->
customProperty( QStringLiteral(
"embedded_project" ) ).toString() );
2703 childGroup->
setCustomProperty( QStringLiteral(
"embedded_project" ), projectPath );
2707 QList<QgsLayerTreeNode *> clonedChildren;
2708 const QList<QgsLayerTreeNode *> constChildren = newGroup->
children();
2709 clonedChildren.reserve( constChildren.size() );
2711 clonedChildren << newGroupChild->clone();
2719 loadEmbeddedNodes( childGroup,
flags );
2724 if ( child->customProperty( QStringLiteral(
"embedded" ) ).toInt() )
2726 QList<QDomNode> brokenNodes;
2729 valid = valid &&
false;
2744 return mCustomVariables;
2751 if ( variables == mCustomVariables )
2755 QStringList variableNames;
2756 QStringList variableValues;
2758 QVariantMap::const_iterator it = variables.constBegin();
2759 for ( ; it != variables.constEnd(); ++it )
2761 variableNames << it.key();
2762 variableValues << it.value().toString();
2765 writeEntry( QStringLiteral(
"Variables" ), QStringLiteral(
"/variableNames" ), variableNames );
2766 writeEntry( QStringLiteral(
"Variables" ), QStringLiteral(
"/variableValues" ), variableValues );
2768 mCustomVariables = variables;
2769 mProjectScope.reset();
2778 *mLabelingEngineSettings = settings;
2786 return *mLabelingEngineSettings;
2793 mProjectScope.reset();
2794 return mLayerStore.get();
2801 return mLayerStore.get();
2808 QList<QgsVectorLayer *>
layers;
2809 const QStringList layerIds =
readListEntry( QStringLiteral(
"Digitizing" ), QStringLiteral(
"/AvoidIntersectionsList" ), QStringList() );
2810 const auto constLayerIds = layerIds;
2811 for (
const QString &layerId : constLayerIds )
2824 list.reserve(
layers.size() );
2826 list << layer->id();
2827 writeEntry( QStringLiteral(
"Digitizing" ), QStringLiteral(
"/AvoidIntersectionsList" ), list );
2849 if ( mProjectScope )
2851 std::unique_ptr< QgsExpressionContextScope > projectScope = std::make_unique< QgsExpressionContextScope >( *mProjectScope );
2858 projectScope->addFunction( QStringLiteral(
"sensor_data" ),
new GetSensorData(
sensorManager()->sensorsData() ) );
2860 return projectScope.release();
2863 mProjectScope = std::make_unique< QgsExpressionContextScope >( QObject::tr(
"Project" ) );
2867 QVariantMap::const_iterator it = vars.constBegin();
2869 for ( ; it != vars.constEnd(); ++it )
2871 mProjectScope->setVariable( it.key(), it.value(),
true );
2875 if ( projectPath.isEmpty() )
2876 projectPath = mOriginalPath;
2877 const QString projectFolder = QFileInfo( projectPath ).path();
2878 const QString projectFilename = QFileInfo( projectPath ).fileName();
2879 const QString projectBasename =
baseName();
2916 QVariantMap keywords;
2918 for (
auto it = metadataKeywords.constBegin(); it != metadataKeywords.constEnd(); ++it )
2920 keywords.insert( it.key(), it.value() );
2925 QVariantList layersIds;
2927 const QMap<QString, QgsMapLayer *> layersInProject = mLayerStore->mapLayers();
2928 layersIds.reserve( layersInProject.count() );
2929 layers.reserve( layersInProject.count() );
2930 for (
auto it = layersInProject.constBegin(); it != layersInProject.constEnd(); ++it )
2932 layersIds << it.value()->id();
2938 mProjectScope->addFunction( QStringLiteral(
"project_color" ),
new GetNamedProjectColor(
this ) );
2943void QgsProject::onMapLayersAdded(
const QList<QgsMapLayer *> &layers )
2947 const QMap<QString, QgsMapLayer *> existingMaps =
mapLayers();
2949 const auto constLayers =
layers;
2952 if ( ! layer->isValid() )
2955 if (
QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( layer ) )
2958 if ( vlayer->dataProvider() )
2966 for ( QMap<QString, QgsMapLayer *>::const_iterator it = existingMaps.cbegin(); it != existingMaps.cend(); ++it )
2968 const QSet<QgsMapLayerDependency> deps = it.value()->dependencies();
2969 if ( deps.contains( layer->id() ) )
2972 it.value()->setDependencies( deps );
2977 updateTransactionGroups();
2983void QgsProject::onMapLayersRemoved(
const QList<QgsMapLayer *> &layers )
2991void QgsProject::cleanTransactionGroups(
bool force )
2995 bool changed =
false;
2996 for ( QMap< QPair< QString, QString>,
QgsTransactionGroup *>::Iterator tg = mTransactionGroups.begin(); tg != mTransactionGroups.end(); )
2998 if ( tg.value()->isEmpty() || force )
3001 tg = mTransactionGroups.erase( tg );
3013void QgsProject::updateTransactionGroups()
3017 mEditBufferGroup.
clear();
3019 switch ( mTransactionMode )
3023 cleanTransactionGroups(
true );
3028 cleanTransactionGroups(
true );
3031 cleanTransactionGroups(
false );
3035 bool tgChanged =
false;
3036 const auto constLayers =
mapLayers().values();
3039 if ( ! layer->isValid() )
3042 QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( layer );
3046 switch ( mTransactionMode )
3063 mTransactionGroups.insert( qMakePair( key, connString ), tg );
3073 mEditBufferGroup.
addLayer( vlayer );
3089 context.setProjectTranslator(
this );
3091 QList<QDomNode> brokenNodes;
3092 if ( addLayer( layerNode.toElement(), brokenNodes, context ) )
3096 const QVector<QgsVectorLayer *> vectorLayers = layers<QgsVectorLayer *>();
3100 layer->resolveReferences(
this );
3102 if ( layer->isValid() && layer->customProperty( QStringLiteral(
"_layer_was_editable" ) ).toBool() )
3104 layer->startEditing();
3105 layer->removeCustomProperty( QStringLiteral(
"_layer_was_editable" ) );
3117 mFile.setFileName( filename );
3118 mCachedHomePath.clear();
3126 mProjectScope.reset();
3132 const QString storageFilePath { storage->filePath( mFile.fileName() ) };
3133 if ( storageFilePath.isEmpty() )
3139 const QString tempPath = QStandardPaths::standardLocations( QStandardPaths::TempLocation ).at( 0 );
3140 const QString tmpZipFilename( tempPath + QDir::separator() + QUuid::createUuid().toString() );
3142 if ( !zip( tmpZipFilename ) )
3145 QFile tmpZipFile( tmpZipFilename );
3146 if ( !tmpZipFile.open( QIODevice::ReadOnly ) )
3148 setError( tr(
"Unable to read file %1" ).arg( tmpZipFilename ) );
3153 if ( !storage->writeProject( mFile.fileName(), &tmpZipFile, context ) )
3155 QString err = tr(
"Unable to save project to storage %1" ).arg( mFile.fileName() );
3156 QList<QgsReadWriteContext::ReadWriteMessage> messages = context.
takeMessages();
3157 if ( !messages.isEmpty() )
3158 err += QStringLiteral(
"\n\n" ) + messages.last().message();
3164 QFile::remove( tmpZipFilename );
3171 return zip( mFile.fileName() );
3177 const bool asOk = saveAuxiliaryStorage();
3178 const bool writeOk = writeProjectFile( mFile.fileName() );
3179 bool attachmentsOk =
true;
3180 if ( !mArchive->files().isEmpty() )
3182 const QFileInfo finfo( mFile.fileName() );
3183 const QString attachmentsZip = finfo.absoluteDir().absoluteFilePath( QStringLiteral(
"%1_attachments.zip" ).arg( finfo.completeBaseName() ) );
3184 attachmentsOk = mArchive->zip( attachmentsZip );
3188 if ( ( !asOk || !attachmentsOk ) && writeOk )
3190 QStringList errorMessage;
3193 const QString err = mAuxiliaryStorage->errorString();
3194 errorMessage.append( tr(
"Unable to save auxiliary storage ('%1')" ).arg( err ) );
3196 if ( !attachmentsOk )
3198 errorMessage.append( tr(
"Unable to save attachments archive" ) );
3200 setError( errorMessage.join(
'\n' ) );
3203 return asOk && writeOk && attachmentsOk;
3207bool QgsProject::writeProjectFile(
const QString &filename )
3211 QFile projectFile( filename );
3217 const QFileInfo myFileInfo( projectFile );
3218 if ( myFileInfo.exists() && !myFileInfo.isWritable() )
3220 setError( tr(
"%1 is not writable. Please adjust permissions (if possible) and try again." )
3221 .arg( projectFile.fileName() ) );
3229 QDomImplementation::setInvalidDataPolicy( QDomImplementation::DropInvalidChars );
3231 const QDomDocumentType documentType =
3232 QDomImplementation().createDocumentType( QStringLiteral(
"qgis" ), QStringLiteral(
"http://mrcc.com/qgis.dtd" ),
3233 QStringLiteral(
"SYSTEM" ) );
3234 std::unique_ptr<QDomDocument> doc(
new QDomDocument( documentType ) );
3236 QDomElement qgisNode = doc->createElement( QStringLiteral(
"qgis" ) );
3237 qgisNode.setAttribute( QStringLiteral(
"projectname" ),
title() );
3238 qgisNode.setAttribute( QStringLiteral(
"version" ),
Qgis::version() );
3240 if ( !mSettings.
value( QStringLiteral(
"projects/anonymize_saved_projects" ),
false,
QgsSettings::Core ).toBool() )
3244 qgisNode.setAttribute( QStringLiteral(
"saveUser" ), newSaveUser );
3245 qgisNode.setAttribute( QStringLiteral(
"saveUserFull" ), newSaveUserFull );
3246 mSaveUser = newSaveUser;
3247 mSaveUserFull = newSaveUserFull;
3248 mSaveDateTime = QDateTime::currentDateTime();
3249 qgisNode.setAttribute( QStringLiteral(
"saveDateTime" ), mSaveDateTime.toString( Qt::ISODate ) );
3254 mSaveUserFull.clear();
3255 mSaveDateTime = QDateTime();
3257 doc->appendChild( qgisNode );
3260 QDomElement homePathNode = doc->createElement( QStringLiteral(
"homePath" ) );
3261 homePathNode.setAttribute( QStringLiteral(
"path" ), mHomePath );
3262 qgisNode.appendChild( homePathNode );
3265 QDomElement titleNode = doc->createElement( QStringLiteral(
"title" ) );
3266 qgisNode.appendChild( titleNode );
3268 QDomElement transactionNode = doc->createElement( QStringLiteral(
"transaction" ) );
3269 transactionNode.setAttribute( QStringLiteral(
"mode" ),
qgsEnumValueToKey( mTransactionMode ) );
3270 qgisNode.appendChild( transactionNode );
3272 QDomElement flagsNode = doc->createElement( QStringLiteral(
"projectFlags" ) );
3274 qgisNode.appendChild( flagsNode );
3276 const QDomText titleText = doc->createTextNode(
title() );
3277 titleNode.appendChild( titleText );
3281 QDomElement srsNode = doc->createElement( QStringLiteral(
"projectCrs" ) );
3283 qgisNode.appendChild( srsNode );
3286 QDomElement verticalSrsNode = doc->createElement( QStringLiteral(
"verticalCrs" ) );
3287 mVerticalCrs.
writeXml( verticalSrsNode, *doc );
3288 qgisNode.appendChild( verticalSrsNode );
3291 QDomElement elevationShadingNode = doc->createElement( QStringLiteral(
"elevation-shading-renderer" ) );
3292 mElevationShadingRenderer.
writeXml( elevationShadingNode, context );
3293 qgisNode.appendChild( elevationShadingNode );
3300 clonedRoot->
writeXml( qgisNode, context );
3304 writeEntry( QStringLiteral(
"Digitizing" ), QStringLiteral(
"/AvoidIntersectionsMode" ),
static_cast<int>( mAvoidIntersectionsMode ) );
3312 QDomElement annotationLayerNode = doc->createElement( QStringLiteral(
"main-annotation-layer" ) );
3313 mMainAnnotationLayer->
writeLayerXml( annotationLayerNode, *doc, context );
3314 qgisNode.appendChild( annotationLayerNode );
3318 QDomElement projectLayersNode = doc->createElement( QStringLiteral(
"projectlayers" ) );
3320 QMap<QString, QgsMapLayer *>::ConstIterator li =
layers.constBegin();
3321 while ( li !=
layers.end() )
3327 const QHash< QString, QPair< QString, bool> >::const_iterator emIt = mEmbeddedLayers.constFind( ml->
id() );
3328 if ( emIt == mEmbeddedLayers.constEnd() )
3330 QDomElement maplayerElem;
3336 maplayerElem = doc->createElement( QStringLiteral(
"maplayer" ) );
3340 maplayerElem.setAttribute( QStringLiteral(
"editable" ), QStringLiteral(
"1" ) );
3344 QDomDocument document;
3347 maplayerElem = document.firstChildElement();
3351 QgsDebugError( QStringLiteral(
"Could not restore layer properties for layer %1" ).arg( ml->
id() ) );
3357 projectLayersNode.appendChild( maplayerElem );
3363 if ( emIt.value().second )
3365 QDomElement mapLayerElem = doc->createElement( QStringLiteral(
"maplayer" ) );
3366 mapLayerElem.setAttribute( QStringLiteral(
"embedded" ), 1 );
3367 mapLayerElem.setAttribute( QStringLiteral(
"project" ),
writePath( emIt.value().first ) );
3368 mapLayerElem.setAttribute( QStringLiteral(
"id" ), ml->
id() );
3369 projectLayersNode.appendChild( mapLayerElem );
3376 qgisNode.appendChild( projectLayersNode );
3378 QDomElement layerOrderNode = doc->createElement( QStringLiteral(
"layerorder" ) );
3380 for (
QgsMapLayer *layer : constCustomLayerOrder )
3382 QDomElement mapLayerElem = doc->createElement( QStringLiteral(
"layer" ) );
3383 mapLayerElem.setAttribute( QStringLiteral(
"id" ), layer->id() );
3384 layerOrderNode.appendChild( mapLayerElem );
3386 qgisNode.appendChild( layerOrderNode );
3388 mLabelingEngineSettings->writeSettingsToProject(
this );
3390 QDomElement labelEngineSettingsElement = doc->createElement( QStringLiteral(
"labelEngineSettings" ) );
3391 mLabelingEngineSettings->writeXml( *doc, labelEngineSettingsElement, context );
3392 qgisNode.appendChild( labelEngineSettingsElement );
3395 writeEntry( QStringLiteral(
"Gui" ), QStringLiteral(
"/CanvasColorRedPart" ), mBackgroundColor.red() );
3396 writeEntry( QStringLiteral(
"Gui" ), QStringLiteral(
"/CanvasColorGreenPart" ), mBackgroundColor.green() );
3397 writeEntry( QStringLiteral(
"Gui" ), QStringLiteral(
"/CanvasColorBluePart" ), mBackgroundColor.blue() );
3399 writeEntry( QStringLiteral(
"Gui" ), QStringLiteral(
"/SelectionColorRedPart" ), mSelectionColor.red() );
3400 writeEntry( QStringLiteral(
"Gui" ), QStringLiteral(
"/SelectionColorGreenPart" ), mSelectionColor.green() );
3401 writeEntry( QStringLiteral(
"Gui" ), QStringLiteral(
"/SelectionColorBluePart" ), mSelectionColor.blue() );
3402 writeEntry( QStringLiteral(
"Gui" ), QStringLiteral(
"/SelectionColorAlphaPart" ), mSelectionColor.alpha() );
3409 dump_( mProperties );
3412 QgsDebugMsgLevel( QStringLiteral(
"there are %1 property scopes" ).arg(
static_cast<int>( mProperties.
count() ) ), 2 );
3417 mProperties.
writeXml( QStringLiteral(
"properties" ), qgisNode, *doc );
3420 QDomElement ddElem = doc->createElement( QStringLiteral(
"dataDefinedServerProperties" ) );
3421 mDataDefinedServerProperties.
writeXml( ddElem, dataDefinedServerPropertyDefinitions() );
3422 qgisNode.appendChild( ddElem );
3424 mMapThemeCollection->writeXml( *doc );
3426 mTransformContext.
writeXml( qgisNode, context );
3428 QDomElement metadataElem = doc->createElement( QStringLiteral(
"projectMetadata" ) );
3430 qgisNode.appendChild( metadataElem );
3433 const QDomElement annotationsElem = mAnnotationManager->writeXml( *doc, context );
3434 qgisNode.appendChild( annotationsElem );
3438 const QDomElement layoutElem = mLayoutManager->writeXml( *doc );
3439 qgisNode.appendChild( layoutElem );
3443 const QDomElement views3DElem = m3DViewsManager->writeXml( *doc );
3444 qgisNode.appendChild( views3DElem );
3448 const QDomElement bookmarkElem = mBookmarkManager->
writeXml( *doc );
3449 qgisNode.appendChild( bookmarkElem );
3453 const QDomElement sensorElem = mSensorManager->
writeXml( *doc );
3454 qgisNode.appendChild( sensorElem );
3458 const QDomElement viewSettingsElem = mViewSettings->
writeXml( *doc, context );
3459 qgisNode.appendChild( viewSettingsElem );
3463 const QDomElement styleSettingsElem = mStyleSettings->
writeXml( *doc, context );
3464 qgisNode.appendChild( styleSettingsElem );
3468 const QDomElement timeSettingsElement = mTimeSettings->
writeXml( *doc, context );
3469 qgisNode.appendChild( timeSettingsElement );
3473 const QDomElement elevationPropertiesElement = mElevationProperties->
writeXml( *doc, context );
3474 qgisNode.appendChild( elevationPropertiesElement );
3478 const QDomElement displaySettingsElem = mDisplaySettings->
writeXml( *doc, context );
3479 qgisNode.appendChild( displaySettingsElem );
3483 const QDomElement gpsSettingsElem = mGpsSettings->
writeXml( *doc, context );
3484 qgisNode.appendChild( gpsSettingsElem );
3493 QFile backupFile( QStringLiteral(
"%1~" ).arg( filename ) );
3495 ok &= backupFile.open( QIODevice::WriteOnly | QIODevice::Truncate );
3496 ok &= projectFile.open( QIODevice::ReadOnly );
3499 while ( ok && !projectFile.atEnd() )
3501 ba = projectFile.read( 10240 );
3502 ok &= backupFile.write( ba ) == ba.size();
3505 projectFile.close();
3510 setError( tr(
"Unable to create backup file %1" ).arg( backupFile.fileName() ) );
3515 struct utimbuf tb = {
static_cast<time_t
>( fi.lastRead().toSecsSinceEpoch() ),
static_cast<time_t
>( fi.lastModified().toSecsSinceEpoch() ) };
3516 utime( backupFile.fileName().toUtf8().constData(), &tb );
3519 if ( !projectFile.open( QIODevice::WriteOnly | QIODevice::Truncate ) )
3521 projectFile.close();
3524 setError( tr(
"Unable to save to file %1" ).arg( projectFile.fileName() ) );
3528 QTemporaryFile tempFile;
3529 bool ok = tempFile.open();
3532 QTextStream projectFileStream( &tempFile );
3533 doc->save( projectFileStream, 2 );
3534 ok &= projectFileStream.pos() > -1;
3536 ok &= tempFile.seek( 0 );
3539 while ( ok && !tempFile.atEnd() )
3541 ba = tempFile.read( 10240 );
3542 ok &= projectFile.write( ba ) == ba.size();
3545 ok &= projectFile.error() == QFile::NoError;
3547 projectFile.close();
3554 setError( tr(
"Unable to save to file %1. Your project "
3555 "may be corrupted on disk. Try clearing some space on the volume and "
3556 "check file permissions before pressing save again." )
3557 .arg( projectFile.fileName() ) );
3571 bool propertiesModified;
3572 const bool success =
addKey_( scope, key, &mProperties, value, propertiesModified );
3574 if ( propertiesModified )
3584 bool propertiesModified;
3585 const bool success =
addKey_( scope, key, &mProperties, value, propertiesModified );
3587 if ( propertiesModified )
3597 bool propertiesModified;
3598 const bool success =
addKey_( scope, key, &mProperties, value, propertiesModified );
3600 if ( propertiesModified )
3610 bool propertiesModified;
3611 const bool success =
addKey_( scope, key, &mProperties, value, propertiesModified );
3613 if ( propertiesModified )
3623 bool propertiesModified;
3624 const bool success =
addKey_( scope, key, &mProperties, value, propertiesModified );
3626 if ( propertiesModified )
3634 const QStringList &def,
3646 value =
property->value();
3648 const bool valid = QMetaType::Type::QStringList == value.userType();
3654 return value.toStringList();
3677 value =
property->value();
3679 const bool valid = value.canConvert( QMetaType::Type::QString );
3684 return value.toString();
3703 value =
property->value();
3706 const bool valid = value.canConvert( QMetaType::Type::Int );
3715 return value.toInt();
3730 const QVariant value =
property->value();
3732 const bool valid = value.canConvert( QMetaType::Type::Double );
3737 return value.toDouble();
3754 const QVariant value =
property->value();
3756 const bool valid = value.canConvert( QMetaType::Type::Bool );
3761 return value.toBool();
3773 if (
findKey_( scope, key, mProperties ) )
3779 return !
findKey_( scope, key, mProperties );
3788 QStringList entries;
3790 if ( foundProperty )
3807 QStringList entries;
3809 if ( foundProperty )
3824 dump_( mProperties );
3844 filePath = storage->filePath( mFile.fileName() );
3871void QgsProject::setError(
const QString &errorMessage )
3875 mErrorMessage = errorMessage;
3882 return mErrorMessage;
3885void QgsProject::clearError()
3889 setError( QString() );
3896 delete mBadLayerHandler;
3897 mBadLayerHandler = handler;
3904 const QHash< QString, QPair< QString, bool > >::const_iterator it = mEmbeddedLayers.find(
id );
3905 if ( it == mEmbeddedLayers.constEnd() )
3909 return it.value().first;
3919 static QString sPrevProjectFilePath;
3920 static QDateTime sPrevProjectFileTimestamp;
3921 static QDomDocument sProjectDocument;
3923 QString qgsProjectFile = projectFilePath;
3925 if ( projectFilePath.endsWith( QLatin1String(
".qgz" ), Qt::CaseInsensitive ) )
3927 archive.
unzip( projectFilePath );
3931 const QDateTime projectFileTimestamp = QFileInfo( projectFilePath ).lastModified();
3933 if ( projectFilePath != sPrevProjectFilePath || projectFileTimestamp != sPrevProjectFileTimestamp )
3935 sPrevProjectFilePath.clear();
3937 QFile projectFile( qgsProjectFile );
3938 if ( !projectFile.open( QIODevice::ReadOnly ) )
3943 if ( !sProjectDocument.setContent( &projectFile ) )
3948 sPrevProjectFilePath = projectFilePath;
3949 sPrevProjectFileTimestamp = projectFileTimestamp;
3953 bool useAbsolutePaths =
true;
3955 const QDomElement propertiesElem = sProjectDocument.documentElement().firstChildElement( QStringLiteral(
"properties" ) );
3956 if ( !propertiesElem.isNull() )
3958 const QDomElement absElem = propertiesElem.firstChildElement( QStringLiteral(
"Paths" ) ).firstChildElement( QStringLiteral(
"Absolute" ) );
3959 if ( !absElem.isNull() )
3961 useAbsolutePaths = absElem.text().compare( QLatin1String(
"true" ), Qt::CaseInsensitive ) == 0;
3966 if ( !useAbsolutePaths )
3971 const QDomElement projectLayersElem = sProjectDocument.documentElement().firstChildElement( QStringLiteral(
"projectlayers" ) );
3972 if ( projectLayersElem.isNull() )
3977 QDomElement mapLayerElem = projectLayersElem.firstChildElement( QStringLiteral(
"maplayer" ) );
3978 while ( ! mapLayerElem.isNull() )
3981 const QString
id = mapLayerElem.firstChildElement( QStringLiteral(
"id" ) ).text();
3982 if (
id == layerId )
3985 if ( mapLayerElem.attribute( QStringLiteral(
"embedded" ) ) == QLatin1String(
"1" ) )
3990 mEmbeddedLayers.insert( layerId, qMakePair( projectFilePath, saveFlag ) );
3992 if ( addLayer( mapLayerElem, brokenNodes, embeddedContext,
flags ) )
3998 mEmbeddedLayers.remove( layerId );
4002 mapLayerElem = mapLayerElem.nextSiblingElement( QStringLiteral(
"maplayer" ) );
4012 QString qgsProjectFile = projectFilePath;
4014 if ( projectFilePath.endsWith( QLatin1String(
".qgz" ), Qt::CaseInsensitive ) )
4016 archive.
unzip( projectFilePath );
4021 QFile projectFile( qgsProjectFile );
4022 if ( !projectFile.open( QIODevice::ReadOnly ) )
4027 QDomDocument projectDocument;
4028 if ( !projectDocument.setContent( &projectFile ) )
4040 QDomElement layerTreeElem = projectDocument.documentElement().firstChildElement( QStringLiteral(
"layer-tree-group" ) );
4041 if ( !layerTreeElem.isNull() )
4051 if ( !group || group->
customProperty( QStringLiteral(
"embedded" ) ).toBool() )
4064 newGroup->
setCustomProperty( QStringLiteral(
"embedded_project" ), projectFilePath );
4067 mLayerTreeRegistryBridge->
setEnabled(
false );
4068 initializeEmbeddedSubtree( projectFilePath, newGroup,
flags );
4069 mLayerTreeRegistryBridge->
setEnabled(
true );
4072 const auto constFindLayerIds = newGroup->
findLayerIds();
4073 for (
const QString &layerId : constFindLayerIds )
4090 const auto constChildren = group->
children();
4094 child->setCustomProperty( QStringLiteral(
"embedded" ), 1 );
4103 QList<QDomNode> brokenNodes;
4127 writeEntry( QStringLiteral(
"Digitizing" ), QStringLiteral(
"/TopologicalEditing" ), ( enabled ? 1 : 0 ) );
4135 return readNumEntry( QStringLiteral(
"Digitizing" ), QStringLiteral(
"/TopologicalEditing" ), 0 );
4142 if ( mDistanceUnits == unit )
4145 mDistanceUnits = unit;
4154 if ( mAreaUnits == unit )
4167 if ( !mCachedHomePath.isEmpty() )
4168 return mCachedHomePath;
4172 if ( !mHomePath.isEmpty() )
4174 const QFileInfo homeInfo( mHomePath );
4175 if ( !homeInfo.isRelative() )
4177 mCachedHomePath = mHomePath;
4187 const QString storagePath { storage->filePath(
fileName() ) };
4188 if ( ! storagePath.isEmpty() && QFileInfo::exists( storagePath ) )
4190 mCachedHomePath = QFileInfo( storagePath ).path();
4191 return mCachedHomePath;
4195 mCachedHomePath = pfi.path();
4196 return mCachedHomePath;
4199 if ( !pfi.exists() )
4201 mCachedHomePath = mHomePath;
4205 if ( !mHomePath.isEmpty() )
4208 mCachedHomePath = QDir::cleanPath( pfi.path() +
'/' + mHomePath );
4212 mCachedHomePath = pfi.canonicalPath();
4214 return mCachedHomePath;
4229 return mRelationManager;
4236 return mLayoutManager.get();
4243 return mLayoutManager.get();
4250 return m3DViewsManager.get();
4257 return m3DViewsManager.get();
4264 return mBookmarkManager;
4271 return mBookmarkManager;
4278 return mSensorManager;
4285 return mSensorManager;
4292 return mViewSettings;
4299 return mViewSettings;
4306 return mStyleSettings;
4314 return mStyleSettings;
4321 return mTimeSettings;
4328 return mTimeSettings;
4335 return mElevationProperties;
4342 return mElevationProperties;
4349 return mDisplaySettings;
4356 return mDisplaySettings;
4363 return mGpsSettings;
4370 return mGpsSettings;
4384 return mMapThemeCollection.get();
4391 return mAnnotationManager.get();
4398 return mAnnotationManager.get();
4405 const QMap<QString, QgsMapLayer *> &projectLayers =
mapLayers();
4406 for ( QMap<QString, QgsMapLayer *>::const_iterator it = projectLayers.constBegin(); it != projectLayers.constEnd(); ++it )
4411 if (
layers.contains( it.value() ) )
4412 it.value()->setFlags( it.value()->flags() &
~QgsMapLayer::Identifiable );
4428 for (
const QString &layerId : layerIds )
4446 for ( QMap<QString, QgsMapLayer *>::const_iterator it =
layers.constBegin(); it !=
layers.constEnd(); ++it )
4480 updateTransactionGroups();
4487 return mTransactionMode;
4498 const auto constLayers =
mapLayers().values();
4501 if ( layer->isEditable() )
4503 QgsLogger::warning( tr(
"Transaction mode can be changed only if all layers are not editable." ) );
4509 updateTransactionGroups();
4518 return mTransactionGroups;
4531 return mLayerStore->count();
4538 return mLayerStore->validCount();
4546 return mLayerStore->mapLayer( layerId );
4553 return mLayerStore->mapLayersByName( layerName );
4560 QList<QgsMapLayer *>
layers;
4561 const auto constMapLayers { mLayerStore->mapLayers() };
4562 for (
const auto &l : constMapLayers )
4564 if ( ! l->serverProperties()->shortName().isEmpty() )
4566 if ( l->serverProperties()->shortName() == shortName )
4569 else if ( l->name() == shortName )
4585 if ( !archive->unzip( filename ) )
4587 setError( tr(
"Unable to unzip file '%1'" ).arg( filename ) );
4592 if ( archive->projectFile().isEmpty() )
4594 setError( tr(
"Zip archive does not provide a project file" ) );
4599 releaseHandlesToProjectArchive();
4600 mArchive = std::move( archive );
4617 setError( tr(
"Cannot read unzipped qgs project file" ) + QStringLiteral(
": " ) +
error() );
4627bool QgsProject::zip(
const QString &filename )
4635 const QString
baseName = QFileInfo( filename ).baseName();
4636 const QString qgsFileName = QStringLiteral(
"%1.qgs" ).arg(
baseName );
4637 QFile qgsFile( QDir( archive->dir() ).filePath( qgsFileName ) );
4639 bool writeOk =
false;
4640 if ( qgsFile.open( QIODevice::WriteOnly | QIODevice::Truncate ) )
4642 writeOk = writeProjectFile( qgsFile.fileName() );
4649 setError( tr(
"Unable to write temporary qgs file" ) );
4654 const QFileInfo info( qgsFile );
4656 const QString asFileName = info.path() + QDir::separator() + info.completeBaseName() + asExt;
4658 bool auxiliaryStorageSavedOk =
true;
4659 if ( ! saveAuxiliaryStorage( asFileName ) )
4661 const QString err = mAuxiliaryStorage->errorString();
4662 setError( tr(
"Unable to save auxiliary storage file ('%1'). The project has been saved but the latest changes to auxiliary data cannot be recovered. It is recommended to reload the project." ).arg( err ) );
4663 auxiliaryStorageSavedOk =
false;
4666 if ( !mArchive->exists() )
4668 releaseHandlesToProjectArchive();
4670 mArchive->unzip( mFile.fileName() );
4673 const QString auxiliaryStorageFile =
static_cast<QgsProjectArchive *
>( mArchive.get() )->auxiliaryStorageFile();
4674 if ( ! auxiliaryStorageFile.isEmpty() )
4676 archive->
addFile( auxiliaryStorageFile );
4685 if ( QFile::exists( asFileName ) )
4687 archive->addFile( asFileName );
4692 archive->addFile( qgsFile.fileName() );
4695 const QStringList &
files = mArchive->files();
4696 for (
const QString &file :
files )
4698 if ( !file.endsWith( QLatin1String(
".qgs" ), Qt::CaseInsensitive ) && !file.endsWith( asExt, Qt::CaseInsensitive ) )
4700 archive->addFile( file );
4706 if ( !archive->zip( filename ) )
4708 setError( tr(
"Unable to perform zip" ) );
4712 return auxiliaryStorageSavedOk && zipOk;
4723 const QList<QgsMapLayer *> &layers,
4725 bool takeOwnership )
4729 const QList<QgsMapLayer *> myResultList { mLayerStore->addMapLayers(
layers, takeOwnership ) };
4730 if ( !myResultList.isEmpty() )
4733 for (
auto &l : myResultList )
4743 if ( mAuxiliaryStorage )
4758 mProjectScope.reset();
4760 return myResultList;
4766 bool takeOwnership )
4770 QList<QgsMapLayer *> addedLayers;
4771 addedLayers =
addMapLayers( QList<QgsMapLayer *>() << layer, addToLegend, takeOwnership );
4772 return addedLayers.isEmpty() ? nullptr : addedLayers[0];
4775void QgsProject::removeAuxiliaryLayer(
const QgsMapLayer *ml )
4782 const QgsVectorLayer *vl = qobject_cast<const QgsVectorLayer *>( ml );
4794 for (
const auto &layerId : layerIds )
4795 removeAuxiliaryLayer( mLayerStore->mapLayer( layerId ) );
4797 mProjectScope.reset();
4798 mLayerStore->removeMapLayers( layerIds );
4805 for (
const auto &layer :
layers )
4806 removeAuxiliaryLayer( layer );
4808 mProjectScope.reset();
4809 mLayerStore->removeMapLayers(
layers );
4816 removeAuxiliaryLayer( mLayerStore->mapLayer( layerId ) );
4817 mProjectScope.reset();
4818 mLayerStore->removeMapLayer( layerId );
4825 removeAuxiliaryLayer( layer );
4826 mProjectScope.reset();
4827 mLayerStore->removeMapLayer( layer );
4834 mProjectScope.reset();
4835 return mLayerStore->takeMapLayer( layer );
4842 return mMainAnnotationLayer;
4849 if ( mLayerStore->count() == 0 )
4852 ScopedIntIncrementor snapSingleBlocker( &mBlockSnappingUpdates );
4853 mProjectScope.reset();
4854 mLayerStore->removeAllMapLayers();
4856 snapSingleBlocker.release();
4858 if ( !mBlockSnappingUpdates )
4866 const QMap<QString, QgsMapLayer *>
layers = mLayerStore->mapLayers();
4867 QMap<QString, QgsMapLayer *>::const_iterator it =
layers.constBegin();
4868 for ( ; it !=
layers.constEnd(); ++it )
4870 it.value()->reload();
4879 return validOnly ? mLayerStore->validMapLayers() : mLayerStore->mapLayers();
4886 return mTransactionGroups.value( qMakePair( providerKey, connString ) );
4893 return &mEditBufferGroup;
4904 if ( mSettings.
value( QStringLiteral(
"/projections/unknownCrsBehavior" ), QStringLiteral(
"NoAction" ),
QgsSettings::App ).toString() == QStringLiteral(
"UseProjectCrs" )
4905 || mSettings.
value( QStringLiteral(
"/projections/unknownCrsBehavior" ), 0,
QgsSettings::App ).toString() == QLatin1String(
"2" ) )
4913 const QString layerDefaultCrs = mSettings.
value( QStringLiteral(
"/Projections/layerDefaultCrs" ),
geoEpsgCrsAuthId() ).toString();
4934bool QgsProject::saveAuxiliaryStorage(
const QString &filename )
4940 for (
auto it =
layers.constBegin(); it !=
layers.constEnd(); ++it )
4945 QgsVectorLayer *vl = qobject_cast<QgsVectorLayer *>( it.value() );
4953 if ( !mAuxiliaryStorage->exists( *
this ) && empty )
4957 else if ( !filename.isEmpty() )
4959 return mAuxiliaryStorage->saveAs( filename );
4963 return mAuxiliaryStorage->saveAs( *
this );
4976 return sPropertyDefinitions;
4989 return mAuxiliaryStorage.get();
4996 return mAuxiliaryStorage.get();
5003 const QDir archiveDir( mArchive->dir() );
5004 QTemporaryFile tmpFile( archiveDir.filePath(
"XXXXXX_" + nameTemplate ),
this );
5005 tmpFile.setAutoRemove(
false );
5007 mArchive->addFile( tmpFile.fileName() );
5008 return tmpFile.fileName();
5015 QStringList attachments;
5017 const QStringList files = mArchive->files();
5018 attachments.reserve( files.size() );
5019 for (
const QString &file : files )
5021 if ( QFileInfo( file ).baseName() !=
baseName )
5023 attachments.append( file );
5033 return mArchive->removeFile( path );
5040 return QStringLiteral(
"attachment:///%1" ).arg( QFileInfo( attachedFile ).
fileName() );
5047 if ( identifier.startsWith( QLatin1String(
"attachment:///" ) ) )
5049 return QDir( mArchive->dir() ).absoluteFilePath( identifier.mid( 14 ) );
5070 mProjectScope.reset();
5084 for ( QMap<QString, QgsMapLayer *>::const_iterator it =
layers.constBegin(); it !=
layers.constEnd(); ++it )
5098 const QMap<QString, QgsMapLayer *> &projectLayers =
mapLayers();
5099 for ( QMap<QString, QgsMapLayer *>::const_iterator it = projectLayers.constBegin(); it != projectLayers.constEnd(); ++it )
5104 if (
layers.contains( it.value() ) )
5105 it.value()->setFlags( it.value()->flags() &
~QgsMapLayer::Removable );
5116 QStringList customColors;
5117 QStringList customColorLabels;
5119 QgsNamedColorList::const_iterator colorIt = colors.constBegin();
5120 for ( ; colorIt != colors.constEnd(); ++colorIt )
5123 const QString label = ( *colorIt ).second;
5124 customColors.append( color );
5125 customColorLabels.append( label );
5127 writeEntry( QStringLiteral(
"Palette" ), QStringLiteral(
"/Colors" ), customColors );
5128 writeEntry( QStringLiteral(
"Palette" ), QStringLiteral(
"/Labels" ), customColorLabels );
5129 mProjectScope.reset();
5137 if ( mBackgroundColor == color )
5140 mBackgroundColor = color;
5148 return mBackgroundColor;
5155 if ( mSelectionColor == color )
5158 mSelectionColor = color;
5166 return mSelectionColor;
5203 translationContext.setFileName( QStringLiteral(
"%1/%2.ts" ).arg(
absolutePath(),
baseName() ) );
5207 translationContext.writeTsFile( locale );
5210QString
QgsProject::translate(
const QString &context,
const QString &sourceText,
const char *disambiguation,
int n )
const
5219 QString result = mTranslator->translate( context.toUtf8(), sourceText.toUtf8(), disambiguation, n );
5221 if ( result.isEmpty() )
5235 for (
auto it =
layers.constBegin(); it !=
layers.constEnd(); ++it )
5240 if ( !( ( *it )->accept( visitor ) ) )
5249 if ( !mLayoutManager->accept( visitor ) )
5252 if ( !mAnnotationManager->accept( visitor ) )
5260 return mElevationShadingRenderer;
5263void QgsProject::loadProjectFlags(
const QDomDocument *doc )
5267 QDomElement element = doc->documentElement().firstChildElement( QStringLiteral(
"projectFlags" ) );
5269 if ( !element.isNull() )
5276 element = doc->documentElement().firstChildElement( QStringLiteral(
"evaluateDefaultValues" ) );
5277 if ( !element.isNull() )
5279 if ( element.attribute( QStringLiteral(
"active" ), QStringLiteral(
"0" ) ).toInt() == 1 )
5284 element = doc->documentElement().firstChildElement( QStringLiteral(
"trust" ) );
5285 if ( !element.isNull() )
5287 if ( element.attribute( QStringLiteral(
"active" ), QStringLiteral(
"0" ) ).toInt() == 1 )
5303 const QString projectFunctions =
readEntry( QStringLiteral(
"ExpressionFunctions" ), QStringLiteral(
"/pythonCode" ), QString() );
5304 if ( !projectFunctions.isEmpty() )
5323GetNamedProjectColor::GetNamedProjectColor(
const QgsProject *project )
5330 QStringList colorStrings = project->
readListEntry( QStringLiteral(
"Palette" ), QStringLiteral(
"/Colors" ) );
5331 const QStringList colorLabels = project->
readListEntry( QStringLiteral(
"Palette" ), QStringLiteral(
"/Labels" ) );
5335 for ( QStringList::iterator it = colorStrings.begin();
5336 it != colorStrings.end(); ++it )
5340 if ( colorLabels.length() > colorIndex )
5342 label = colorLabels.at( colorIndex );
5345 mColors.insert( label.toLower(), color );
5350GetNamedProjectColor::GetNamedProjectColor(
const QHash<QString, QColor> &colors )
5358 const QString colorName = values.at( 0 ).toString().toLower();
5359 if ( mColors.contains( colorName ) )
5361 return QStringLiteral(
"%1,%2,%3" ).arg( mColors.value( colorName ).red() ).arg( mColors.value( colorName ).green() ).arg( mColors.value( colorName ).blue() );
5369 return new GetNamedProjectColor( mColors );
5374GetSensorData::GetSensorData(
const QMap<QString, QgsAbstractSensor::SensorData> &sensorData )
5377 QStringLiteral(
"Sensors" ) )
5378 , mSensorData( sensorData )
5384 const QString sensorName = values.at( 0 ).toString();
5385 const int expiration = values.at( 1 ).toInt();
5386 const qint64 timestamp = QDateTime::currentMSecsSinceEpoch();
5387 if ( mSensorData.contains( sensorName ) )
5389 if ( expiration <= 0 || ( timestamp - mSensorData[sensorName].lastTimestamp.toMSecsSinceEpoch() ) < expiration )
5391 return mSensorData[sensorName].lastValue;
5400 return new GetSensorData( mSensorData );
@ DontLoad3DViews
Skip loading 3D views.
@ DontStoreOriginalStyles
Skip the initial XML style storage for layers. Useful for minimising project load times in non-intera...
@ ForceReadOnlyLayers
Open layers in a read-only mode.
@ TrustLayerMetadata
Trust layer metadata. Improves project read time. Do not use it if layers' extent is not fixed during...
@ DontUpgradeAnnotations
Don't upgrade old annotation items to QgsAnnotationItem.
@ DontLoadLayouts
Don't load print layouts. Improves project read time if layouts are not required, and allows projects...
@ DontResolveLayers
Don't resolve layer paths (i.e. don't load any layer content). Dramatically improves project read tim...
static QString version()
Version string.
QFlags< ProjectCapability > ProjectCapabilities
Flags which control project capabilities.
QFlags< ProjectReadFlag > ProjectReadFlags
Project load flags.
DistanceUnit
Units of distance.
FilePathType
File path types.
TransactionMode
Transaction mode.
@ AutomaticGroups
Automatic transactional editing means that on supported datasources (postgres and geopackage database...
@ BufferedGroups
Buffered transactional editing means that all editable layers in the buffered transaction group are t...
@ Disabled
Edits are buffered locally and sent to the provider when toggling layer editing mode.
@ SquareMeters
Square meters.
@ Critical
Critical/error message.
@ Success
Used for reporting a successful operation.
PythonEmbeddedMode
Authorisation to run Python Embedded in projects.
@ Always
Python embedded is always run.
@ Ask
User is prompt before running.
@ SessionOnly
Only during this session.
@ Compound
Compound (horizontal + vertical) CRS.
@ Projected
Projected CRS.
@ DerivedProjected
Derived projected CRS.
@ Engineering
Engineering CRS.
@ Geographic3d
3D geopraphic CRS
@ Geographic2d
2D geographic CRS
@ Geocentric
Geocentric CRS.
AvoidIntersectionsMode
Flags which control how intersections of pre-existing feature are handled when digitizing new feature...
@ AvoidIntersectionsLayers
Overlap with features from a specified list of layers when digitizing new features not allowed.
@ AllowIntersections
Overlap with any feature allowed when digitizing new features.
ProjectFlag
Flags which control the behavior of QgsProjects.
@ RememberLayerEditStatusBetweenSessions
If set, then any layers set to be editable will be stored in the project and immediately made editabl...
@ EvaluateDefaultValuesOnProviderSide
If set, default values for fields will be evaluated on the provider side when features from the proje...
@ TrustStoredLayerStatistics
If set, then layer statistics (such as the layer extent) will be read from values stored in the proje...
QFlags< DataProviderReadFlag > DataProviderReadFlags
Flags which control data provider construction.
LayerType
Types of layers that can be added to a map.
@ Group
Composite group layer. Added in QGIS 3.24.
@ Plugin
Plugin based layer.
@ TiledScene
Tiled scene layer. Added in QGIS 3.34.
@ Annotation
Contains freeform, georeferenced annotations. Added in QGIS 3.16.
@ VectorTile
Vector tile layer. Added in QGIS 3.14.
@ Mesh
Mesh layer. Added in QGIS 3.2.
@ PointCloud
Point cloud layer. Added in QGIS 3.18.
@ SkipCredentialsRequest
Skip credentials if the provided one are not valid, let the provider be invalid, avoiding to block th...
@ ParallelThreadLoading
Provider is created in a parallel thread than the one where it will live.
QFlags< ProjectFlag > ProjectFlags
@ Preferred
Preferred format, matching the most recent WKT ISO standard. Currently an alias to WKT2_2019,...
virtual bool readXml(const QDomElement &collectionElem, const QgsPropertiesDefinition &definitions)
Reads property collection state from an XML element.
virtual bool writeXml(QDomElement &collectionElem, const QgsPropertiesDefinition &definitions) const
Writes the current state of the property collection into an XML element.
Represents a map layer containing a set of georeferenced annotations, e.g.
void resolveReferences(QgsProject *project) override
Resolve references to other layers (kept as layer IDs after reading XML) into layer objects.
void setTransformContext(const QgsCoordinateTransformContext &context) override
Sets the coordinate transform context to transformContext.
void reset()
Resets the annotation layer to a default state, and clears all items from it.
bool isEmpty() const
Returns true if the annotation layer is empty and contains no annotations.
Manages storage of a set of QgsAnnotation annotation objects.
static QgsApplication * instance()
Returns the singleton instance of the QgsApplication.
static QgsProjectStorageRegistry * projectStorageRegistry()
Returns registry of available project storage implementations.
static const QgsSettingsEntryString * settingsLocaleUserLocale
Settings entry locale user locale.
static QgsRuntimeProfiler * profiler()
Returns the application runtime profiler.
void collectTranslatableObjects(QgsTranslationContext *translationContext)
Emits the signal to collect all the strings of .qgs to be included in ts file.
static QgsPluginLayerRegistry * pluginLayerRegistry()
Returns the application's plugin layer registry, used for managing plugin layer types.
void requestForTranslatableObjects(QgsTranslationContext *translationContext)
Emitted when project strings which require translation are being collected for inclusion in a ....
static QString userFullName()
Returns the user's operating system login account full display name.
static QString userLoginName()
Returns the user's operating system login account name.
Class allowing to manage the zip/unzip actions.
void addFile(const QString &filename)
Add a new file to this archive.
This is a container for attribute editors, used to group them visually in the attribute form if it is...
QList< QgsAttributeEditorElement * > children() const
Gets a list of the children elements of this container.
This is an abstract base class for any elements of a drag and drop form.
QString name() const
Returns the name of this element.
QgsFields auxiliaryFields() const
Returns a list of all auxiliary fields currently managed by the layer.
bool save()
Commits changes and starts editing then.
Class providing some utility methods to manage auxiliary storage.
static QString extension()
Returns the extension used for auxiliary databases.
static bool deleteTable(const QgsDataSourceUri &uri)
Removes a table from the auxiliary storage.
Manages storage of a set of bookmarks.
bool readXml(const QDomElement &element, const QDomDocument &doc)
Reads the manager's state from a DOM element, restoring all bookmarks present in the XML document.
void clear()
Removes and deletes all bookmarks from the manager.
QDomElement writeXml(QDomDocument &doc) const
Returns a DOM element representing the state of the manager.
static QColor colorFromString(const QString &string)
Decodes a string into a color value.
static QString colorToString(const QColor &color)
Encodes a color into a string value.
This class represents a coordinate reference system (CRS).
static QgsCoordinateReferenceSystem fromOgcWmsCrs(const QString &ogcCrs)
Creates a CRS from a given OGC WMS-format Coordinate Reference System string.
bool isValid() const
Returns whether this CRS is correctly initialized and usable.
bool hasVerticalAxis() const
Returns true if the CRS has a vertical axis.
QString toProj() const
Returns a Proj string representation of this CRS.
bool readXml(const QDomNode &node)
Restores state from the given DOM node.
static QgsCoordinateReferenceSystem createCompoundCrs(const QgsCoordinateReferenceSystem &horizontalCrs, const QgsCoordinateReferenceSystem &verticalCrs, QString &error)
Given a horizontal and vertical CRS, attempts to create a compound CRS from them.
QString ellipsoidAcronym() const
Returns the ellipsoid acronym for the ellipsoid used by the CRS.
QString projectionAcronym() const
Returns the projection acronym for the projection used by the CRS.
QgsCoordinateReferenceSystem verticalCrs() const
Returns the vertical CRS associated with this CRS object.
static QgsCoordinateReferenceSystem fromProj(const QString &proj)
Creates a CRS from a proj style formatted string.
QString toWkt(Qgis::CrsWktVariant variant=Qgis::CrsWktVariant::Wkt1Gdal, bool multiline=false, int indentationWidth=4) const
Returns a WKT representation of this CRS.
bool writeXml(QDomNode &node, QDomDocument &doc) const
Stores state to the given Dom node in the given document.
static QgsCoordinateReferenceSystem fromSrsId(long srsId)
Creates a CRS from a specified QGIS SRS ID.
Qgis::CrsType type() const
Returns the type of the CRS.
Qgis::DistanceUnit mapUnits
Contains information about the context in which a coordinate transform is executed.
void readSettings()
Reads the context's state from application settings.
void writeXml(QDomElement &element, const QgsReadWriteContext &context) const
Writes the context's state to a DOM element.
bool readXml(const QDomElement &element, const QgsReadWriteContext &context, QStringList &missingTransforms)
Reads the context's state from a DOM element.
Abstract base class for spatial data provider implementations.
@ EvaluateDefaultValues
Evaluate default values on provider side when calling QgsVectorDataProvider::defaultValue( int index ...
Class for storing the component parts of a RDBMS data source URI (e.g.
This class can render elevation shading on an image with different methods (eye dome lighting,...
void writeXml(QDomElement &elem, const QgsReadWriteContext &context) const
Writes configuration on a DOM element.
void readXml(const QDomElement &element, const QgsReadWriteContext &context)
Reads configuration from a DOM element.
Single scope for storing variables and functions for use within a QgsExpressionContext.
static QgsExpressionContextScope * projectScope(const QgsProject *project)
Creates a new scope which contains variables and functions relating to a QGIS project.
static QgsExpressionContextScope * globalScope()
Creates a new scope which contains variables and functions relating to the global QGIS context.
Expression contexts are used to encapsulate the parameters around which a QgsExpression should be eva...
A abstract base class for defining QgsExpression functions.
An expression node for expression functions.
Class for parsing and evaluation of expressions (formerly called "search strings").
Encapsulate a field in an attribute table or data source.
Container of fields for a vector layer.
Stores global configuration for labeling engine.
Class used to work with layer dependencies stored in a XML project or layer definition file.
Layer tree group node serves as a container for layers and further groups.
void resolveReferences(const QgsProject *project, bool looseMatching=false) override
Calls resolveReferences() on child tree nodes.
QgsLayerTreeGroup * findGroup(const QString &name)
Find group node with specified name.
QList< QgsLayerTreeGroup * > findGroups(bool recursive=false) const
Find group layer nodes.
QString name() const override
Returns the group's name.
QStringList findLayerIds() const
Find layer IDs used in all layer nodes.
void insertChildNodes(int index, const QList< QgsLayerTreeNode * > &nodes)
Insert existing nodes at specified position.
void readChildrenFromXml(QDomElement &element, const QgsReadWriteContext &context)
Read children from XML and append them to the group.
QgsLayerTreeGroup * clone() const override
Returns a clone of the group.
QList< QgsLayerTreeLayer * > findLayers() const
Find all layer nodes.
QgsLayerTreeLayer * findLayer(QgsMapLayer *layer) const
Find layer node representing the map layer.
Layer tree node points to a map layer.
void resolveReferences(const QgsProject *project, bool looseMatching=false) override
Resolves reference to layer from stored layer ID (if it has not been resolved already)
This class is a base class for nodes in a layer tree.
QList< QgsLayerTreeNode * > abandonChildren()
Removes the children, disconnect all the forwarded and external signals and sets their parent to null...
void setCustomProperty(const QString &key, const QVariant &value)
Sets a custom property for the node. Properties are stored in a map and saved in project file.
virtual void writeXml(QDomElement &parentElement, const QgsReadWriteContext &context)=0
Write layer tree to XML.
QList< QgsLayerTreeNode * > children()
Gets list of children of the node. Children are owned by the parent.
void removeCustomProperty(const QString &key)
Remove a custom property from layer. Properties are stored in a map and saved in project file.
QVariant customProperty(const QString &key, const QVariant &defaultValue=QVariant()) const
Read a custom property from layer. Properties are stored in a map and saved in project file.
void setItemVisibilityChecked(bool checked)
Check or uncheck a node (independently of its ancestors or children)
Listens to the updates in map layer registry and does changes in layer tree.
void setEnabled(bool enabled)
static void replaceChildrenOfEmbeddedGroups(QgsLayerTreeGroup *group)
Remove subtree of embedded groups and replaces it with a custom property embedded-visible-layers.
static void storeOriginalLayersProperties(QgsLayerTreeGroup *group, const QDomDocument *doc)
Stores in a layer's originalXmlProperties the layer properties information.
static void updateEmbeddedGroupsProjectPath(QgsLayerTreeGroup *group, const QgsProject *project)
Updates an embedded group from a project.
static bool readOldLegend(QgsLayerTreeGroup *root, const QDomElement &legendElem)
Try to load layer tree from.
Namespace with helper functions for layer tree operations.
void readLayerOrderFromXml(const QDomElement &doc)
Load the layer order from an XML element.
static QgsLayerTreeLayer * toLayer(QgsLayerTreeNode *node)
Cast node to a layer.
void clear()
Clear any information from this layer tree.
static bool isLayer(const QgsLayerTreeNode *node)
Check whether the node is a valid layer node.
static bool isGroup(QgsLayerTreeNode *node)
Check whether the node is a valid group node.
static QgsLayerTreeGroup * toGroup(QgsLayerTreeNode *node)
Cast node to a group.
QList< QgsMapLayer * > customLayerOrder() const
The order in which layers will be rendered on the canvas.
QgsLayerTree * clone() const override
Create a copy of the node. Returns new instance.
Manages storage of a set of layouts.