Skip to content
Permalink
Browse files

[FEATURE] Allow overwriting the project home path

This allows the project home path (which is used by the browser
to create the 'Project Home' item) to be set by users for a
project, instead of always matching the location where the project
is saved.

This allows users to set the project home to a folder which contains
data and other content, and is especially useful for organisations
where qgis projects are not stored in the root folder of a organisational
'project'.

Project home paths can also be set to relative paths, in which
case they will be relative to the project saved location.

The path can be set through the Project Properties dialog, or
by right-clicking on the Project Home browser item and
selecting 'set project home'

Sponsored by SMEC/SJ
  • Loading branch information
nyalldawson committed Mar 7, 2018
1 parent d09a34c commit 4e5c08e2b54b7f3b06d15f4538aba6a8d9b84ae7
@@ -442,9 +442,34 @@ Sets the default area measurement units for the project.

QString homePath() const;
%Docstring
Return project's home path
Returns the project's home path. This will either be a manually set home path
(see presetHomePath()) or the path containing the project file itself.

:return: home path of project (or null QString if not set) *
This method always returns the absolute path to the project's home. See
presetHomePath() to retrieve any manual project home path override (e.g.
relative home paths).

.. seealso:: :py:func:`setPresetHomePath`

.. seealso:: :py:func:`presetHomePath`

.. seealso:: :py:func:`homePathChanged`
%End

QString presetHomePath() const;
%Docstring
Returns any manual project home path setting, or an empty string if not set.

This path may be a relative path. See homePath() to retrieve a path which is always
an absolute path.

.. seealso:: :py:func:`homePath`

.. seealso:: :py:func:`setPresetHomePath`

.. seealso:: :py:func:`homePathChanged`

.. versionadded:: 3.2
%End

QgsRelationManager *relationManager() const;
@@ -937,7 +962,13 @@ Emitted when the file name of the project changes

void homePathChanged();
%Docstring
Emitted when the home path of the project changes
Emitted when the home path of the project changes.

.. seealso:: :py:func:`setPresetHomePath`

.. seealso:: :py:func:`homePath`

.. seealso:: :py:func:`presetHomePath`
%End

void snappingConfigChanged( const QgsSnappingConfig &config );
@@ -1170,6 +1201,20 @@ be asked to save changes to the project before closing the current project.
promoted to public slot in 2.16
%End

void setPresetHomePath( const QString &path );
%Docstring
Sets the project's home ``path``. If an empty path is specified than the
home path will be automatically determined from the project's file path.

.. versionadded:: 3.2

.. seealso:: :py:func:`presetHomePath`

.. seealso:: :py:func:`homePath`

.. seealso:: :py:func:`homePathChanged`
%End

};


@@ -204,6 +204,41 @@ QgsProjectProperties::QgsProjectProperties( QgsMapCanvas *mapCanvas, QWidget *pa
mAutoTransaction->setChecked( QgsProject::instance()->autoTransaction() );
title( QgsProject::instance()->title() );
mProjectFileLineEdit->setText( QDir::toNativeSeparators( QgsProject::instance()->fileName() ) );
mProjectHomeLineEdit->setShowClearButton( true );
mProjectHomeLineEdit->setText( QDir::toNativeSeparators( QgsProject::instance()->presetHomePath() ) );
connect( mButtonSetProjectHome, &QToolButton::clicked, this, [ = ]
{
auto getAbsoluteHome = [this]()->QString
{
QString currentHome = QDir::fromNativeSeparators( mProjectHomeLineEdit->text() );
if ( !currentHome.isEmpty() )
{
QFileInfo homeInfo( currentHome );
if ( !homeInfo.isRelative() )
return currentHome;
}

QFileInfo pfi( QgsProject::instance()->fileName() );
if ( !pfi.exists() )
return QDir::homePath();

if ( !currentHome.isEmpty() )
{
// path is relative to project file
return QDir::cleanPath( pfi.path() + '/' + currentHome );
}
else
{
return pfi.canonicalPath();
}
};

QString newDir = QFileDialog::getExistingDirectory( this, tr( "Select Project Home Path" ), getAbsoluteHome() );
if ( ! newDir.isNull() )
{
mProjectHomeLineEdit->setText( QDir::toNativeSeparators( newDir ) );
}
} );

connect( mButtonOpenProjectFolder, &QToolButton::clicked, this, [ = ]
{
@@ -833,6 +868,7 @@ void QgsProjectProperties::apply()

// Set the project title
QgsProject::instance()->setTitle( title() );
QgsProject::instance()->setPresetHomePath( QDir::fromNativeSeparators( mProjectHomeLineEdit->text() ) );
QgsProject::instance()->setAutoTransaction( mAutoTransaction->isChecked() );
QgsProject::instance()->setEvaluateDefaultValues( mEvaluateDefaultValues->isChecked() );
QgsProject::instance()->setTrustLayerMetadata( mTrustProjectCheckBox->isChecked() );
@@ -177,7 +177,8 @@ void QgsBrowserModel::initialize()
if ( ! mInitialized )
{
connect( QgsProject::instance(), &QgsProject::readProject, this, &QgsBrowserModel::updateProjectHome );
connect( QgsProject::instance(), &QgsProject::writeProject, this, &QgsBrowserModel::updateProjectHome );
connect( QgsProject::instance(), &QgsProject::projectSaved, this, &QgsBrowserModel::updateProjectHome );
connect( QgsProject::instance(), &QgsProject::homePathChanged, this, &QgsBrowserModel::updateProjectHome );
addRootItems();
mInitialized = true;
}
@@ -28,6 +28,7 @@
#include <QVector>
#include <QStyle>
#include <QDesktopServices>
#include <QFileDialog>

#include "qgis.h"
#include "qgsdataitem.h"
@@ -40,6 +41,7 @@
#include "qgsconfig.h"
#include "qgssettings.h"
#include "qgsanimatedicon.h"
#include "qgsproject.h"

// use GDAL VSI mechanism
#define CPL_SUPRESS_CPLUSPLUS //#spellok
@@ -1614,6 +1616,28 @@ QVariant QgsProjectHomeItem::sortKey() const
return QStringLiteral( " 1" );
}

QList<QAction *> QgsProjectHomeItem::actions( QWidget *parent )
{
QList<QAction *> lst = QgsDirectoryItem::actions( parent );
QAction *separator = new QAction( parent );
separator->setSeparator( true );
lst.append( separator );

QAction *setHome = new QAction( tr( "Set Project Home…" ), parent );
connect( setHome, &QAction::triggered, this, [ = ]
{
QString oldHome = QgsProject::instance()->homePath();
QString newPath = QFileDialog::getExistingDirectory( parent, tr( "Select Project Home Directory" ), oldHome );
if ( !newPath.isEmpty() )
{
QgsProject::instance()->setPresetHomePath( newPath );
}
} );
lst << setHome;

return lst;
}

QgsFavoriteItem::QgsFavoriteItem( QgsFavoritesItem *parent, const QString &name, const QString &dirPath, const QString &path )
: QgsDirectoryItem( parent, name, dirPath, path )
, mFavorites( parent )
@@ -745,6 +745,9 @@ class CORE_EXPORT QgsProjectHomeItem : public QgsDirectoryItem
QIcon icon() override;
QVariant sortKey() const override;

QList<QAction *> actions( QWidget *parent ) override;


};

/**
@@ -417,6 +417,17 @@ void QgsProject::setDirty( bool b )
emit isDirtyChanged( mDirty );
}

void QgsProject::setPresetHomePath( const QString &path )
{
if ( path == mHomePath )
return;

mHomePath = path;
emit homePathChanged();

setDirty( true );
}

void QgsProject::setFileName( const QString &name )
{
if ( name == mFile.fileName() )
@@ -488,6 +499,7 @@ void QgsProject::clear()
mFile.setFileName( QString() );
mProperties.clearKeys();
mTitle.clear();
mHomePath.clear();
mAutoTransaction = false;
mEvaluateDefaultValues = false;
mDirty = false;
@@ -895,6 +907,19 @@ bool QgsProject::readProjectFile( const QString &filename )
// now get project title
_getTitle( *doc, mTitle );

QDomNodeList homePathNl = doc->elementsByTagName( QStringLiteral( "homePath" ) );
if ( homePathNl.count() > 0 )
{
QDomElement homePathElement = homePathNl.at( 0 ).toElement();
QString homePath = homePathElement.attribute( QStringLiteral( "path" ) );
if ( !homePath.isEmpty() )
setPresetHomePath( homePath );
}
else
{
emit homePathChanged();
}

QgsReadWriteContext context;
context.setPathResolver( pathResolver() );

@@ -1370,6 +1395,10 @@ bool QgsProject::writeProjectFile( const QString &filename )

doc->appendChild( qgisNode );

QDomElement homePathNode = doc->createElement( QStringLiteral( "homePath" ) );
homePathNode.setAttribute( QStringLiteral( "path" ), mHomePath );
qgisNode.appendChild( homePathNode );

// title
QDomElement titleNode = doc->createElement( QStringLiteral( "title" ) );
qgisNode.appendChild( titleNode );
@@ -2077,11 +2106,31 @@ void QgsProject::setAreaUnits( QgsUnitTypes::AreaUnit unit )

QString QgsProject::homePath() const
{
if ( !mHomePath.isEmpty() )
{
QFileInfo homeInfo( mHomePath );
if ( !homeInfo.isRelative() )
return mHomePath;
}

QFileInfo pfi( fileName() );
if ( !pfi.exists() )
return QString();
return mHomePath;

return pfi.canonicalPath();
if ( !mHomePath.isEmpty() )
{
// path is relative to project file
return QDir::cleanPath( pfi.path() + '/' + mHomePath );
}
else
{
return pfi.canonicalPath();
}
}

QString QgsProject::presetHomePath() const
{
return mHomePath;
}

QgsRelationManager *QgsProject::relationManager() const
@@ -85,7 +85,7 @@ class CORE_EXPORT QgsProject : public QObject, public QgsExpressionContextGenera
Q_OBJECT
Q_PROPERTY( QStringList nonIdentifiableLayers READ nonIdentifiableLayers WRITE setNonIdentifiableLayers NOTIFY nonIdentifiableLayersChanged )
Q_PROPERTY( QString fileName READ fileName WRITE setFileName NOTIFY fileNameChanged )
Q_PROPERTY( QString homePath READ homePath NOTIFY homePathChanged )
Q_PROPERTY( QString homePath READ homePath WRITE setPresetHomePath NOTIFY homePathChanged )
Q_PROPERTY( QgsCoordinateReferenceSystem crs READ crs WRITE setCrs NOTIFY crsChanged )
Q_PROPERTY( QString ellipsoid READ ellipsoid WRITE setEllipsoid NOTIFY ellipsoidChanged )
Q_PROPERTY( QgsMapThemeCollection *mapThemeCollection READ mapThemeCollection NOTIFY mapThemeCollectionChanged )
@@ -423,10 +423,33 @@ class CORE_EXPORT QgsProject : public QObject, public QgsExpressionContextGenera
void setAreaUnits( QgsUnitTypes::AreaUnit unit );

/**
* Return project's home path
\returns home path of project (or null QString if not set) */
* Returns the project's home path. This will either be a manually set home path
* (see presetHomePath()) or the path containing the project file itself.
*
* This method always returns the absolute path to the project's home. See
* presetHomePath() to retrieve any manual project home path override (e.g.
* relative home paths).
*
* \see setPresetHomePath()
* \see presetHomePath()
* \see homePathChanged()
*/
QString homePath() const;

/**
* Returns any manual project home path setting, or an empty string if not set.
*
* This path may be a relative path. See homePath() to retrieve a path which is always
* an absolute path.
*
* \see homePath()
* \see setPresetHomePath()
* \see homePathChanged()
*
* \since QGIS 3.2
*/
QString presetHomePath() const;

QgsRelationManager *relationManager() const;

/**
@@ -905,7 +928,12 @@ class CORE_EXPORT QgsProject : public QObject, public QgsExpressionContextGenera
//! Emitted when the file name of the project changes
void fileNameChanged();

//! Emitted when the home path of the project changes
/**
* Emitted when the home path of the project changes.
* \see setPresetHomePath()
* \see homePath()
* \see presetHomePath()
*/
void homePathChanged();

//! emitted whenever the configuration for snapping has changed
@@ -1117,6 +1145,16 @@ class CORE_EXPORT QgsProject : public QObject, public QgsExpressionContextGenera
*/
void setDirty( bool b = true );

/**
* Sets the project's home \a path. If an empty path is specified than the
* home path will be automatically determined from the project's file path.
* \since QGIS 3.2
* \see presetHomePath()
* \see homePath()
* \see homePathChanged()
*/
void setPresetHomePath( const QString &path );

private slots:
void onMapLayersAdded( const QList<QgsMapLayer *> &layers );
void onMapLayersRemoved( const QList<QgsMapLayer *> &layers );
@@ -1212,6 +1250,12 @@ class CORE_EXPORT QgsProject : public QObject, public QgsExpressionContextGenera
std::unique_ptr<QgsAuxiliaryStorage> mAuxiliaryStorage;

QFile mFile; // current physical project file

/**
* Manual override for project home path - if empty, home path is automatically
* created based on file name.
*/
QString mHomePath;
mutable QgsProjectPropertyKey mProperties; // property hierarchy, TODO: this shouldn't be mutable
QString mTitle; // project title
bool mAutoTransaction = false; // transaction grouped editing

0 comments on commit 4e5c08e

Please sign in to comment.
You can’t perform that action at this time.