Navigation Menu

Skip to content

Commit

Permalink
Add enum Qgis::FilePathType and add proper stable api for setting
Browse files Browse the repository at this point in the history
whether QgsProject uses absolute or relative paths for file storage
  • Loading branch information
nyalldawson committed Jul 2, 2021
1 parent 6031e75 commit ee141a1
Show file tree
Hide file tree
Showing 11 changed files with 137 additions and 45 deletions.
8 changes: 7 additions & 1 deletion python/core/auto_additions/qgis.py
Expand Up @@ -384,6 +384,12 @@
Qgis.MeshEditingErrorType.FlatFace.__doc__ = "A flat face is present"
Qgis.MeshEditingErrorType.UniqueSharedVertex.__doc__ = "A least two faces share only one vertices"
Qgis.MeshEditingErrorType.InvalidVertex.__doc__ = "An error occurs due to an invalid vertex (for example, vertex index is out of range the available vertex)"
Qgis.MeshEditingErrorType.__doc__ = 'Type of error that can occur during mesh frame editing\n\n.. versionadded:: 3.22\n\n' + '* ``NoError``: ' + Qgis.MeshEditingErrorType.NoError.__doc__ + '\n' + '* ``InvalidFace``: ' + Qgis.MeshEditingErrorType.InvalidFace.__doc__ + '\n' + '* ``FlatFace``: ' + Qgis.MeshEditingErrorType.FlatFace.__doc__ + '\n' + '* ``UniqueSharedVertex``: ' + Qgis.MeshEditingErrorType.UniqueSharedVertex.__doc__ + '\n' + '* ``InvalidVertex``: ' + Qgis.MeshEditingErrorType.InvalidVertex.__doc__
Qgis.MeshEditingErrorType.__doc__ = 'Type of error that can occur during mesh frame editing.\n\n.. versionadded:: 3.22\n\n' + '* ``NoError``: ' + Qgis.MeshEditingErrorType.NoError.__doc__ + '\n' + '* ``InvalidFace``: ' + Qgis.MeshEditingErrorType.InvalidFace.__doc__ + '\n' + '* ``FlatFace``: ' + Qgis.MeshEditingErrorType.FlatFace.__doc__ + '\n' + '* ``UniqueSharedVertex``: ' + Qgis.MeshEditingErrorType.UniqueSharedVertex.__doc__ + '\n' + '* ``InvalidVertex``: ' + Qgis.MeshEditingErrorType.InvalidVertex.__doc__
# --
Qgis.MeshEditingErrorType.baseClass = Qgis
# monkey patching scoped based enum
Qgis.FilePathType.Absolute.__doc__ = "Absolute path"
Qgis.FilePathType.Relative.__doc__ = "Relative path"
Qgis.FilePathType.__doc__ = 'File path types.\n\n.. versionadded:: 3.22\n\n' + '* ``Absolute``: ' + Qgis.FilePathType.Absolute.__doc__ + '\n' + '* ``Relative``: ' + Qgis.FilePathType.Relative.__doc__
# --
Qgis.FilePathType.baseClass = Qgis
19 changes: 19 additions & 0 deletions python/core/auto_generated/project/qgsproject.sip.in
Expand Up @@ -15,6 +15,7 @@




class QgsProject : QObject, QgsExpressionContextGenerator, QgsExpressionContextScopeGenerator, QgsProjectTranslator
{
%Docstring(signature="appended")
Expand Down Expand Up @@ -262,6 +263,24 @@ Returns empty string when the project is stored in a project storage (there is n
Returns the base name of the project file without the path and without extension - derived from :py:func:`~QgsProject.fileName`.

.. versionadded:: 3.2
%End

Qgis::FilePathType filePathStorage() const;
%Docstring
Returns the type of paths used when storing file paths in a QGS/QGZ project file.

.. seealso:: :py:func:`setFilePathStorage`

.. versionadded:: 3.22
%End

void setFilePathStorage( Qgis::FilePathType type );
%Docstring
Sets the ``type`` of paths used when storing file paths in a QGS/QGZ project file.

.. seealso:: :py:func:`filePathStorage`

.. versionadded:: 3.22
%End

QgsCoordinateReferenceSystem crs() const;
Expand Down
6 changes: 6 additions & 0 deletions python/core/auto_generated/qgis.sip.in
Expand Up @@ -281,6 +281,12 @@ The development version
InvalidVertex,
};

enum class FilePathType
{
Absolute,
Relative,
};

static const double DEFAULT_SEARCH_RADIUS_MM;

static const float DEFAULT_MAPTOPIXEL_THRESHOLD;
Expand Down
12 changes: 10 additions & 2 deletions src/app/options/qgsoptions.cpp
Expand Up @@ -826,7 +826,14 @@ QgsOptions::QgsOptions( QWidget *parent, Qt::WindowFlags fl, const QList<QgsOpti
Qgis::PythonMacroMode pyMacroMode = mSettings->enumValue( QStringLiteral( "/qgis/enableMacros" ), Qgis::PythonMacroMode::Ask );
mEnableMacrosComboBox->setCurrentIndex( mEnableMacrosComboBox->findData( QVariant::fromValue( pyMacroMode ) ) );

mDefaultPathsComboBox->setCurrentIndex( mSettings->value( QStringLiteral( "/qgis/defaultProjectPathsRelative" ), QVariant( true ) ).toBool() ? 0 : 1 );
mDefaultPathsComboBox->addItem( tr( "Absolute" ), static_cast< int >( Qgis::FilePathType::Absolute ) );
mDefaultPathsComboBox->addItem( tr( "Relative" ), static_cast< int >( Qgis::FilePathType::Relative ) );
mDefaultPathsComboBox->setCurrentIndex(
mDefaultPathsComboBox->findData(
static_cast< int >(
mSettings->value( QStringLiteral( "/qgis/defaultProjectPathsRelative" ), QVariant( true ) ).toBool() ? Qgis::FilePathType::Relative : Qgis::FilePathType::Absolute )
)
);

QgsProject::FileFormat defaultProjectFileFormat = mSettings->enumValue( QStringLiteral( "/qgis/defaultProjectFileFormat" ), QgsProject::FileFormat::Qgz );
mFileFormatQgzButton->setChecked( defaultProjectFileFormat == QgsProject::FileFormat::Qgz );
Expand Down Expand Up @@ -1655,7 +1662,8 @@ void QgsOptions::saveOptions()
}
mSettings->setEnumValue( QStringLiteral( "/qgis/enableMacros" ), mEnableMacrosComboBox->currentData().value<Qgis::PythonMacroMode>() );

mSettings->setValue( QStringLiteral( "/qgis/defaultProjectPathsRelative" ), mDefaultPathsComboBox->currentIndex() == 0 );
mSettings->setValue( QStringLiteral( "/qgis/defaultProjectPathsRelative" ),
static_cast< Qgis::FilePathType >( mDefaultPathsComboBox->currentData().toInt() ) == Qgis::FilePathType::Relative );

mSettings->setEnumValue( QStringLiteral( "/qgis/defaultProjectFileFormat" ), mFileFormatQgsButton->isChecked() ? QgsProject::FileFormat::Qgs : QgsProject::FileFormat::Qgz );

Expand Down
6 changes: 4 additions & 2 deletions src/app/qgsprojectproperties.cpp
Expand Up @@ -326,7 +326,9 @@ QgsProjectProperties::QgsProjectProperties( QgsMapCanvas *mapCanvas, QWidget *pa
int dp = QgsProject::instance()->readNumEntry( QStringLiteral( "PositionPrecision" ), QStringLiteral( "/DecimalPlaces" ) );
spinBoxDP->setValue( dp );

cbxAbsolutePath->setCurrentIndex( QgsProject::instance()->readBoolEntry( QStringLiteral( "Paths" ), QStringLiteral( "/Absolute" ), true ) ? 0 : 1 );
cbxAbsolutePath->addItem( tr( "Absolute" ), static_cast< int >( Qgis::FilePathType::Absolute ) );
cbxAbsolutePath->addItem( tr( "Relative" ), static_cast< int >( Qgis::FilePathType::Relative ) );
cbxAbsolutePath->setCurrentIndex( cbxAbsolutePath->findData( static_cast< int >( QgsProject::instance()->filePathStorage() ) ) );

// populate combo box with ellipsoids
// selection of the ellipsoid from settings is deferred to a later point, because it would
Expand Down Expand Up @@ -1124,7 +1126,7 @@ void QgsProjectProperties::apply()
QgsUnitTypes::AreaUnit areaUnits = static_cast< QgsUnitTypes::AreaUnit >( mAreaUnitsCombo->currentData().toInt() );
QgsProject::instance()->setAreaUnits( areaUnits );

QgsProject::instance()->writeEntry( QStringLiteral( "Paths" ), QStringLiteral( "/Absolute" ), cbxAbsolutePath->currentIndex() == 0 );
QgsProject::instance()->setFilePathStorage( static_cast< Qgis::FilePathType >( cbxAbsolutePath->currentData().toInt() ) );

if ( mEllipsoidList.at( mEllipsoidIndex ).acronym.startsWith( QLatin1String( "PARAMETER" ) ) )
{
Expand Down
52 changes: 39 additions & 13 deletions src/core/project/qgsproject.cpp
Expand Up @@ -720,6 +720,25 @@ QString QgsProject::baseName() const
}
}

Qgis::FilePathType QgsProject::filePathStorage() const
{
const bool absolutePaths = readBoolEntry( QStringLiteral( "Paths" ), QStringLiteral( "/Absolute" ), false );
return absolutePaths ? Qgis::FilePathType::Absolute : Qgis::FilePathType::Relative;
}

void QgsProject::setFilePathStorage( Qgis::FilePathType type )
{
switch ( type )
{
case Qgis::FilePathType::Absolute:
writeEntry( QStringLiteral( "Paths" ), QStringLiteral( "/Absolute" ), true );
break;
case Qgis::FilePathType::Relative:
writeEntry( QStringLiteral( "Paths" ), QStringLiteral( "/Absolute" ), false );
break;
}
}

QgsCoordinateReferenceSystem QgsProject::crs() const
{
return mCrs;
Expand Down Expand Up @@ -845,7 +864,7 @@ void QgsProject::clear()
writeEntry( QStringLiteral( "PositionPrecision" ), QStringLiteral( "/DecimalPlaces" ), 2 );

bool defaultRelativePaths = mSettings.value( QStringLiteral( "/qgis/defaultProjectPathsRelative" ), true ).toBool();
writeEntry( QStringLiteral( "Paths" ), QStringLiteral( "/Absolute" ), !defaultRelativePaths );
setFilePathStorage( defaultRelativePaths ? Qgis::FilePathType::Relative : Qgis::FilePathType::Absolute );

//copy default units to project
writeEntry( QStringLiteral( "Measurement" ), QStringLiteral( "/DistanceUnits" ), mSettings.value( QStringLiteral( "/qgis/measure/displayunits" ) ).toString() );
Expand Down Expand Up @@ -2128,7 +2147,7 @@ bool QgsProject::write()
QString storageFilePath { storage->filePath( mFile.fileName() ) };
if ( storageFilePath.isEmpty() )
{
writeEntry( QStringLiteral( "Paths" ), QStringLiteral( "/Absolute" ), true );
setFilePathStorage( Qgis::FilePathType::Absolute );
}
context.setPathResolver( pathResolver() );

Expand Down Expand Up @@ -2739,22 +2758,29 @@ void QgsProject::dumpProperties() const

QgsPathResolver QgsProject::pathResolver() const
{
bool absolutePaths = readBoolEntry( QStringLiteral( "Paths" ), QStringLiteral( "/Absolute" ), false );
QString filePath;
if ( ! absolutePaths )
switch ( filePathStorage() )
{
// for projects stored in a custom storage, we need to ask to the
// storage for the path, if the storage returns an empty path
// relative paths are not supported
if ( QgsProjectStorage *storage = projectStorage() )
{
filePath = storage->filePath( mFile.fileName() );
}
else
case Qgis::FilePathType::Absolute:
break;

case Qgis::FilePathType::Relative:
{
filePath = fileName();
// for projects stored in a custom storage, we need to ask to the
// storage for the path, if the storage returns an empty path
// relative paths are not supported
if ( QgsProjectStorage *storage = projectStorage() )
{
filePath = storage->filePath( mFile.fileName() );
}
else
{
filePath = fileName();
}
break;
}
}

return QgsPathResolver( filePath, mArchive->dir() );
}

Expand Down
18 changes: 18 additions & 0 deletions src/core/project/qgsproject.h
Expand Up @@ -23,6 +23,8 @@

#include "qgis_core.h"
#include "qgis_sip.h"
#include "qgis.h"

#include <memory>
#include <QHash>
#include <QList>
Expand Down Expand Up @@ -334,6 +336,22 @@ class CORE_EXPORT QgsProject : public QObject, public QgsExpressionContextGenera
*/
QString baseName() const;

/**
* Returns the type of paths used when storing file paths in a QGS/QGZ project file.
*
* \see setFilePathStorage()
* \since QGIS 3.22
*/
Qgis::FilePathType filePathStorage() const;

/**
* Sets the \a type of paths used when storing file paths in a QGS/QGZ project file.
*
* \see filePathStorage()
* \since QGIS 3.22
*/
void setFilePathStorage( Qgis::FilePathType type );

/**
* Returns the project's native coordinate reference system.
* \see setCrs()
Expand Down
15 changes: 14 additions & 1 deletion src/core/qgis.h
Expand Up @@ -404,7 +404,8 @@ class CORE_EXPORT Qgis
Q_ENUM( RasterResamplingStage )

/**
* Type of error that can occur during mesh frame editing
* Type of error that can occur during mesh frame editing.
*
* \since QGIS 3.22
*/
enum class MeshEditingErrorType : int
Expand All @@ -417,6 +418,18 @@ class CORE_EXPORT Qgis
};
Q_ENUM( MeshEditingErrorType )

/**
* File path types.
*
* \since QGIS 3.22
*/
enum class FilePathType : int
{
Absolute, //!< Absolute path
Relative, //!< Relative path
};
Q_ENUM( FilePathType )

/**
* Identify search radius in mm
* \since QGIS 2.3
Expand Down
13 changes: 1 addition & 12 deletions src/ui/qgsoptionsbase.ui
Expand Up @@ -1047,18 +1047,7 @@
</widget>
</item>
<item>
<widget class="QComboBox" name="mDefaultPathsComboBox">
<item>
<property name="text">
<string>Relative</string>
</property>
</item>
<item>
<property name="text">
<string>Absolute</string>
</property>
</item>
</widget>
<widget class="QComboBox" name="mDefaultPathsComboBox"/>
</item>
<item>
<spacer name="horizontalSpacer_24">
Expand Down
13 changes: 1 addition & 12 deletions src/ui/qgsprojectpropertiesbase.ui
Expand Up @@ -452,18 +452,7 @@
</layout>
</item>
<item row="4" column="1">
<widget class="QComboBox" name="cbxAbsolutePath">
<item>
<property name="text">
<string>Absolute</string>
</property>
</item>
<item>
<property name="text">
<string>Relative</string>
</property>
</item>
</widget>
<widget class="QComboBox" name="cbxAbsolutePath"/>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_30">
Expand Down
20 changes: 18 additions & 2 deletions tests/src/core/testqgsproject.cpp
Expand Up @@ -54,6 +54,7 @@ class TestQgsProject : public QObject
void projectSaveUser();
void testCrsExpressions();
void testCrsValidAfterReadingProjectFile();
void testFilePathType();
void testDefaultRelativePaths();
void testAttachmentsQgs();
void testAttachmentsQgz();
Expand Down Expand Up @@ -660,6 +661,16 @@ void TestQgsProject::testCrsValidAfterReadingProjectFile()
QCOMPARE( crsChangedSpy.count(), 2 );
}

void TestQgsProject::testFilePathType()
{
QgsProject p;
p.setFilePathStorage( Qgis::FilePathType::Absolute );
QCOMPARE( p.filePathStorage(), Qgis::FilePathType::Absolute );

p.setFilePathStorage( Qgis::FilePathType::Relative );
QCOMPARE( p.filePathStorage(), Qgis::FilePathType::Relative );
}

void TestQgsProject::testCrsExpressions()
{
QgsProject p;
Expand Down Expand Up @@ -709,15 +720,20 @@ void TestQgsProject::testDefaultRelativePaths()

s.setValue( QStringLiteral( "/qgis/defaultProjectPathsRelative" ), true );
QgsProject p1;
bool p1PathsAbsolute = p1.readBoolEntry( QStringLiteral( "Paths" ), QStringLiteral( "/Absolute" ), false );
const bool p1PathsAbsolute = p1.readBoolEntry( QStringLiteral( "Paths" ), QStringLiteral( "/Absolute" ), false );
const Qgis::FilePathType p1Type = p1.filePathStorage();

s.setValue( QStringLiteral( "/qgis/defaultProjectPathsRelative" ), false );
p1.clear();
bool p1PathsAbsolute_2 = p1.readBoolEntry( QStringLiteral( "Paths" ), QStringLiteral( "/Absolute" ), false );
const bool p1PathsAbsolute_2 = p1.readBoolEntry( QStringLiteral( "Paths" ), QStringLiteral( "/Absolute" ), false );
const Qgis::FilePathType p2Type = p1.filePathStorage();

s.setValue( QStringLiteral( "/qgis/defaultProjectPathsRelative" ), bk_defaultRelativePaths );

QCOMPARE( p1PathsAbsolute, false );
QCOMPARE( p1PathsAbsolute_2, true );
QCOMPARE( p1Type, Qgis::FilePathType::Relative );
QCOMPARE( p2Type, Qgis::FilePathType::Absolute );
}

void TestQgsProject::testAttachmentsQgs()
Expand Down

0 comments on commit ee141a1

Please sign in to comment.