[feature] QgsSettings QGIS QSettings replacement (#4160)

* [feature] QgsSettings QGIS QSettings replacement

This is the new QgsSettings class that adds an (optional)
Global Settings additional ini file where a system administrator
can store default values for the settings and provide
pre-configuration.

If an ini file named qgis_global_settings.ini is found
in the pkgDataPath directory it is automatically loaded,
this path can be overriden by a command line argument
( --globalsettingsfile path ) and through an environment
variable (QGIS_GLOBAL_SETTINGS_FILE).
This commit is contained in:
Alessandro Pasotti 2017-02-22 11:22:10 +01:00 committed by Nathan Woodrow
parent 49114aea27
commit e1ede700a8
10 changed files with 913 additions and 12 deletions

View File

@ -15,6 +15,7 @@
%Include qgsapplication.sip
%Include qgsaction.sip
%Include qgssettings.sip
%Include qgsactionmanager.sip
%Include qgsactionscope.sip
%Include qgsactionscoperegistry.sip

181
python/core/qgssettings.sip Normal file
View File

@ -0,0 +1,181 @@
/***************************************************************************
qgssettings.sip
--------------------------------------
Date : January 2017
Copyright : (C) 2017 by Alessandro Pasotti
Email : apasotti at boundlessgeo dot com
***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/
/** \ingroup core
* \class QgsSettings
*
* This class is a composition of two QSettings instances:
* - the main QSettings instance is the standard User Settings and
* - the second one (Global Settings) is meant to provide read-only
* pre-configuration and defaults to the first one.
*
* Unlike the original QSettings, the keys of QgsSettings are case insensitive.
*
* For a given settings key, the function call to value(key, default) will return
* the first existing setting in the order specified below:
* - User Settings
* - Global Settings
* - Default Value
*
* The path to the Global Settings storage can be set before constructing the QgsSettings
* objects, with a static call to:
* static bool setGlobalSettingsPath( QString path );
*
* QgsSettings provides some shortcuts to get/set namespaced settings from/to a specific section:
* - Core
* - Gui
* - Server
* - Plugins
* - Misc
*
* @note added in QGIS 3
*/
class QgsSettings : public QObject
{
%TypeHeaderCode
#include <qgssettings.h>
%End
public:
//! Sections for namespaced settings
enum Section
{
NoSection,
Core,
Gui,
Server,
Plugins,
Misc
};
/** Construct a QgsSettings object for accessing settings of the application
* called application from the organization called organization, and with parent parent.
*/
explicit QgsSettings( const QString &organization,
const QString &application = QString(), QObject *parent = 0 );
/** Construct a QgsSettings object for accessing settings of the application called application
* from the organization called organization, and with parent parent.
* If scope is QSettings::UserScope, the QSettings object searches user-specific settings first,
* before it searches system-wide settings as a fallback. If scope is QSettings::SystemScope,
* the QSettings object ignores user-specific settings and provides access to system-wide settings.
*
* The storage format is set to QSettings::NativeFormat (i.e. calling setDefaultFormat() before
* calling this constructor has no effect).
*
* If no application name is given, the QSettings object will only access the organization-wide
* locations.
*/
QgsSettings( QSettings::Scope scope, const QString &organization,
const QString &application = QString(), QObject *parent = 0 );
/** Construct a QgsSettings object for accessing settings of the application called application
* from the organization called organization, and with parent parent.
* If scope is QSettings::UserScope, the QSettings object searches user-specific settings first,
* before it searches system-wide settings as a fallback. If scope is QSettings::SystemScope,
* the QSettings object ignores user-specific settings and provides access to system-wide settings.
* If format is QSettings::NativeFormat, the native API is used for storing settings. If format
* is QSettings::IniFormat, the INI format is used.
*
* If no application name is given, the QSettings object will only access the organization-wide
* locations.
*/
QgsSettings( QSettings::Format format, QSettings::Scope scope, const QString &organization,
const QString &application = QString(), QObject *parent = 0 );
/** Construct a QgsSettings object for accessing the settings stored in the file called fileName,
* with parent parent. If the file doesn't already exist, it is created.
*
* If format is QSettings::NativeFormat, the meaning of fileName depends on the platform. On Unix,
* fileName is the name of an INI file. On macOS and iOS, fileName is the name of a .plist file.
* On Windows, fileName is a path in the system registry.
*
* If format is QSettings::IniFormat, fileName is the name of an INI file.
*
* Warning: This function is provided for convenience. It works well for accessing INI or .plist
* files generated by Qt, but might fail on some syntaxes found in such files originated by
* other programs. In particular, be aware of the following limitations:
* - QgsSettings provides no way of reading INI "path" entries, i.e., entries with unescaped slash characters.
* (This is because these entries are ambiguous and cannot be resolved automatically.)
* - In INI files, QSettings uses the @ character as a metacharacter in some contexts, to encode
* Qt-specific data types (e.g., \@Rect), and might therefore misinterpret it when it occurs
* in pure INI files.
*/
QgsSettings( const QString &fileName, QSettings::Format format, QObject *parent = 0 );
/** Constructs a QgsSettings object for accessing settings of the application and organization
* set previously with a call to QCoreApplication::setOrganizationName(),
* QCoreApplication::setOrganizationDomain(), and QCoreApplication::setApplicationName().
*
* The scope is QSettings::UserScope and the format is defaultFormat() (QSettings::NativeFormat
* by default). Use setDefaultFormat() before calling this constructor to change the default
* format used by this constructor.
*/
explicit QgsSettings( QObject *parent = 0 );
~QgsSettings();
/** Appends prefix to the current group.
* The current group is automatically prepended to all keys specified to QSettings.
* In addition, query functions such as childGroups(), childKeys(), and allKeys()
* are based on the group. By default, no group is set.
*/
void beginGroup( const QString &prefix );
//! Resets the group to what it was before the corresponding beginGroup() call.
void endGroup();
//! Returns a list of all keys, including subkeys, that can be read using the QSettings object.
QStringList allKeys() const;
//! Returns a list of all top-level keys that can be read using the QSettings object.
QStringList childKeys() const;
//! Returns a list of all key top-level groups that contain keys that can be read using the QSettings object.
QStringList childGroups() const;
//! Return the path to the Global Settings QSettings storage file
static QString globalSettingsPath();
//! Set the Global Settings QSettings storage file
static bool setGlobalSettingsPath( QString path );
//! Adds prefix to the current group and starts reading from an array. Returns the size of the array.
int beginReadArray( const QString &prefix );
//! Closes the array that was started using beginReadArray() or beginWriteArray().
void endArray();
//! Sets the current array index to i. Calls to functions such as setValue(), value(), remove(), and contains() will operate on the array entry at that index.
void setArrayIndex( int i );
//! Sets the value of setting key to value. If the key already exists, the previous value is overwritten.
//! An optional Section argument can be used to set a value to a specific Section.
//! @note keys are case insensitive
void setValue(const QString &key, const QVariant &value, const QgsSettings::Section section = QgsSettings::Section::NoSection );
/** Returns the value for setting key. If the setting doesn't exist, it will be
* searched in the Global Settings and if not found, returns defaultValue.
* If no default value is specified, a default QVariant is returned.
* An optional Section argument can be used to get a value from a specific Section.
*/
QVariant value( const QString &key, const QVariant &defaultValue = QVariant(),
const QgsSettings::Section section = QgsSettings::Section::NoSection ) const;
//! Removes the setting key and any sub-settings of key.
void remove(const QString &key);
//! Return the sanitized and prefixed key
QString prefixedKey( const QString &key, const Section section ) const;
//! Returns the path where settings written using this QSettings object are stored.
QString fileName() const;
//! Writes any unsaved changes to permanent storage, and reloads any settings that have been
//! changed in the meantime by another application.
//! This function is called automatically from QSettings's destructor and by the event
//! loop at regular intervals, so you normally don't need to call it yourself.
void sync();
//! Returns true if there exists a setting called key; returns false otherwise.
//! If a group is set using beginGroup(), key is taken to be relative to that group.
bool contains(const QString &key) const;
};

View File

@ -78,6 +78,7 @@ typedef SInt32 SRefCon;
#endif
#include "qgscustomization.h"
#include "qgssettings.h"
#include "qgsfontutils.h"
#include "qgspluginregistry.h"
#include "qgsmessagelog.h"
@ -119,7 +120,8 @@ void usage( const QString& appName )
<< QStringLiteral( "\t[--noversioncheck]\tdon't check for new version of QGIS at startup\n" )
<< QStringLiteral( "\t[--noplugins]\tdon't restore plugins on startup\n" )
<< QStringLiteral( "\t[--nocustomization]\tdon't apply GUI customization\n" )
<< QStringLiteral( "\t[--customizationfile]\tuse the given ini file as GUI customization\n" )
<< QStringLiteral( "\t[--customizationfile path]\tuse the given ini file as GUI customization\n" )
<< QStringLiteral( "\t[--globalsettingsfile path]\tuse the given ini file as Global Settings (defaults)\n" )
<< QStringLiteral( "\t[--optionspath path]\tuse the given QSettings path\n" )
<< QStringLiteral( "\t[--configpath path]\tuse the given path for all user configuration\n" )
<< QStringLiteral( "\t[--authdbdirectory path] use the given directory for authentication database\n" )
@ -556,6 +558,7 @@ int main( int argc, char *argv[] )
QString pythonfile;
QString customizationfile;
QString globalsettingsfile;
#if defined(ANDROID)
QgsDebugMsg( QString( "Android: All params stripped" ) );// Param %1" ).arg( argv[0] ) );
@ -642,6 +645,10 @@ int main( int argc, char *argv[] )
{
customizationfile = QDir::toNativeSeparators( QFileInfo( args[++i] ).absoluteFilePath() );
}
else if ( i + 1 < argc && ( arg == QLatin1String( "--globalsettingsfile" ) || arg == QLatin1String( "-g" ) ) )
{
globalsettingsfile = QDir::toNativeSeparators( QFileInfo( args[++i] ).absoluteFilePath() );
}
else if ( arg == QLatin1String( "--defaultui" ) || arg == QLatin1String( "-d" ) )
{
myRestoreDefaultWindowState = true;
@ -813,6 +820,35 @@ int main( int argc, char *argv[] )
QCoreApplication::setApplicationName( QgsApplication::QGIS_APPLICATION_NAME );
QCoreApplication::setAttribute( Qt::AA_DontShowIconsInMenus, false );
// SetUp the QgsSettings Global Settings:
// - use the path specified with --globalsettings path,
// - use the environment if not found
// - use a default location as a fallback
if ( globalsettingsfile.isEmpty( ) )
{
globalsettingsfile = getenv( "QGIS_GLOBAL_SETTINGS_FILE" );
}
if ( globalsettingsfile.isEmpty( ) )
{
QString default_globalsettingsfile = QgsApplication::pkgDataPath( ) + "/qgis_global_settings.ini";
if ( QFile::exists( default_globalsettingsfile ) )
{
globalsettingsfile = default_globalsettingsfile;
}
}
if ( !globalsettingsfile.isEmpty() )
{
if ( ! QgsSettings::setGlobalSettingsPath( globalsettingsfile ) )
{
QgsMessageLog::logMessage( QString( "Invalid globalsettingsfile path: %1" ).arg( globalsettingsfile ), QStringLiteral( "QGIS" ) );
}
else
{
QgsMessageLog::logMessage( QString( "Successfully loaded globalsettingsfile path: %1" ).arg( globalsettingsfile ), QStringLiteral( "QGIS" ) );
}
}
// TODO: use QgsSettings
QSettings* customizationsettings = nullptr;
if ( !optionpath.isEmpty() || !configpath.isEmpty() )
{
@ -866,7 +902,8 @@ int main( int argc, char *argv[] )
}
#endif
QSettings mySettings;
QgsSettings mySettings;
// update any saved setting for older themes to new default 'gis' theme (2013-04-15)
if ( mySettings.contains( QStringLiteral( "/Themes" ) ) )
@ -880,7 +917,6 @@ int main( int argc, char *argv[] )
}
}
// custom environment variables
QMap<QString, QString> systemEnvVars = QgsApplication::systemEnvVars();
bool useCustomVars = mySettings.value( QStringLiteral( "qgis/customEnvVarsUse" ), QVariant( false ) ).toBool();
@ -1072,7 +1108,7 @@ int main( int argc, char *argv[] )
// set max. thread count
// this should be done in QgsApplication::init() but it doesn't know the settings dir.
QgsApplication::setMaxThreads( QSettings().value( QStringLiteral( "/qgis/max_threads" ), -1 ).toInt() );
QgsApplication::setMaxThreads( mySettings.value( QStringLiteral( "/qgis/max_threads" ), -1 ).toInt() );
QgisApp *qgis = new QgisApp( mypSplash, myRestorePlugins, mySkipVersionCheck ); // "QgisApp" used to find canonical instance
qgis->setObjectName( QStringLiteral( "QgisApp" ) );

View File

@ -50,7 +50,6 @@
#include <QProgressDialog>
#include <QRegExp>
#include <QRegExpValidator>
#include <QSettings>
#include <QShortcut>
#include <QSpinBox>
#include <QSplashScreen>
@ -70,6 +69,7 @@
#include <QWhatsThis>
#include <QWidgetAction>
#include <qgssettings.h>
#include <qgsnetworkaccessmanager.h>
#include <qgsapplication.h>
#include <qgscomposition.h>
@ -673,7 +673,7 @@ QgisApp::QgisApp( QSplashScreen *splash, bool restorePlugins, bool skipVersionCh
mSplash->showMessage( tr( "Setting up the GUI" ), Qt::AlignHCenter | Qt::AlignBottom );
qApp->processEvents();
QSettings settings;
QgsSettings settings;
startProfile( QStringLiteral( "Building style sheet" ) );
// set up stylesheet builder and apply saved or default style options

View File

@ -257,6 +257,7 @@ SET(QGIS_CORE_SRCS
qgsvirtuallayerdefinitionutils.cpp
qgsmapthemecollection.cpp
qgsxmlutils.cpp
qgssettings.cpp
composer/qgsaddremoveitemcommand.cpp
composer/qgsaddremovemultiframecommand.cpp
@ -539,6 +540,7 @@ SET(QGIS_CORE_MOC_HDRS
qgsmapthemecollection.h
qgswebpage.h
qgswebview.h
qgssettings.h
annotations/qgsannotation.h
annotations/qgsannotationmanager.h

259
src/core/qgssettings.cpp Normal file
View File

@ -0,0 +1,259 @@
/***************************************************************************
qgssettings.cpp
--------------------------------------
Date : January 2017
Copyright : (C) 2017 by Alessandro Pasotti
Email : apasotti at boundlessgeo dot com
***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/
#include <stdlib.h>
#include "qgssettings.h"
#include <QFileInfo>
#include <QSettings>
#include <QDir>
QString QgsSettings::sGlobalSettingsPath = QString();
bool QgsSettings::setGlobalSettingsPath( QString path )
{
if ( QFileInfo::exists( path ) )
{
sGlobalSettingsPath = path;
return true;
}
return false;
}
void QgsSettings::init()
{
if ( ! sGlobalSettingsPath.isEmpty( ) )
{
mGlobalSettings = new QSettings( sGlobalSettingsPath, QSettings::IniFormat );
mGlobalSettings->setIniCodec( "UTF-8" );
}
}
QgsSettings::QgsSettings( const QString &organization, const QString &application, QObject *parent )
{
mUserSettings = new QSettings( organization, application, parent );
init( );
}
QgsSettings::QgsSettings( QSettings::Scope scope, const QString &organization,
const QString &application, QObject *parent )
{
mUserSettings = new QSettings( scope, organization, application, parent );
init( );
}
QgsSettings::QgsSettings( QSettings::Format format, QSettings::Scope scope,
const QString &organization, const QString &application, QObject *parent )
{
mUserSettings = new QSettings( format, scope, organization, application, parent );
init( );
}
QgsSettings::QgsSettings( const QString &fileName, QSettings::Format format, QObject *parent )
{
mUserSettings = new QSettings( fileName, format, parent );
init( );
}
QgsSettings::QgsSettings( QObject *parent )
{
mUserSettings = new QSettings( parent );
init( );
}
QgsSettings::~QgsSettings()
{
delete mUserSettings;
delete mGlobalSettings;
}
void QgsSettings::beginGroup( const QString &prefix )
{
mUserSettings->beginGroup( prefix );
if ( mGlobalSettings )
{
mGlobalSettings->beginGroup( prefix );
}
}
void QgsSettings::endGroup()
{
mUserSettings->endGroup( );
if ( mGlobalSettings )
{
mGlobalSettings->endGroup( );
}
}
QStringList QgsSettings::allKeys() const
{
QStringList keys = mUserSettings->allKeys( );
if ( mGlobalSettings )
{
for ( auto &s : mGlobalSettings->allKeys() )
{
if ( ! keys.contains( s ) )
{
keys.append( s );
}
}
}
return keys;
}
QStringList QgsSettings::childKeys() const
{
QStringList keys = mUserSettings->childKeys( );
if ( mGlobalSettings )
{
for ( auto &s : mGlobalSettings->childKeys() )
{
if ( ! keys.contains( s ) )
{
keys.append( s );
}
}
}
return keys;
}
QStringList QgsSettings::childGroups() const
{
QStringList keys = mUserSettings->childGroups( );
if ( mGlobalSettings )
{
for ( auto &s : mGlobalSettings->childGroups() )
{
if ( ! keys.contains( s ) )
{
keys.append( s );
}
}
}
return keys;
}
QVariant QgsSettings::value( const QString &key, const QVariant &defaultValue, const QgsSettings::Section section ) const
{
QString pKey = prefixedKey( key, section );
if ( ! mUserSettings->value( pKey ).isNull() )
{
return mUserSettings->value( pKey );
}
if ( mGlobalSettings )
{
return mGlobalSettings->value( pKey, defaultValue );
}
return defaultValue;
}
bool QgsSettings::contains( const QString &key ) const
{
return mUserSettings->contains( key ) ||
( mGlobalSettings && mGlobalSettings->contains( key ) );
}
QString QgsSettings::fileName() const
{
return mUserSettings->fileName( );
}
void QgsSettings::sync()
{
return mUserSettings->sync( );
}
void QgsSettings::remove( const QString &key )
{
mGlobalSettings->remove( key );
}
QString QgsSettings::prefixedKey( const QString &key, const Section section ) const
{
QString prefix;
switch ( section )
{
case Section::Core :
prefix = "core";
break;
case Section::Server :
prefix = "server";
break;
case Section::Gui :
prefix = "gui";
break;
case Section::Plugins :
prefix = "plugins";
break;
case Section::Misc :
prefix = "misc";
break;
case Section::NoSection:
default:
return sanitizeKey( key );
}
return prefix + "/" + sanitizeKey( key );
}
int QgsSettings::beginReadArray( const QString &prefix )
{
int size = mUserSettings->beginReadArray( prefix );
if ( 0 == size && mGlobalSettings )
{
size = mGlobalSettings->beginReadArray( prefix );
mUsingGlobalArray = ( size > 0 );
}
return size;
}
void QgsSettings::endArray()
{
mUserSettings->endArray();
if ( mGlobalSettings )
{
mGlobalSettings->endArray();
}
mUsingGlobalArray = false;
}
void QgsSettings::setArrayIndex( int i )
{
if ( mGlobalSettings && mUsingGlobalArray )
{
mGlobalSettings->setArrayIndex( i );
}
else
{
mUserSettings->setArrayIndex( i );
}
}
void QgsSettings::setValue( const QString &key, const QVariant &value , const QgsSettings::Section section )
{
// TODO: add valueChanged signal
mUserSettings->setValue( prefixedKey( key, section ), value );
}
// To lower case and clean the path
QString QgsSettings::sanitizeKey( QString key ) const
{
return QDir::cleanPath( key.toLower() );
}

198
src/core/qgssettings.h Normal file
View File

@ -0,0 +1,198 @@
/***************************************************************************
qgssettings.h
--------------------------------------
Date : January 2017
Copyright : (C) 2017 by Alessandro Pasotti
Email : apasotti at boundlessgeo dot com
***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/
#ifndef QGSSETTINGS_H
#define QGSSETTINGS_H
#include <QSettings>
#include "qgis_core.h"
/** \ingroup core
* \class QgsSettings
*
* This class is a composition of two QSettings instances:
* - the main QSettings instance is the standard User Settings and
* - the second one (Global Settings) is meant to provide read-only
* pre-configuration and defaults to the first one.
*
* Unlike the original QSettings, the keys of QgsSettings are case insensitive.
*
* For a given settings key, the function call to value(key, default) will return
* the first existing setting in the order specified below:
* - User Settings
* - Global Settings
* - Default Value
*
* The path to the Global Settings storage can be set before constructing the QgsSettings
* objects, with a static call to:
* static bool setGlobalSettingsPath( QString path );
*
* QgsSettings provides some shortcuts to get/set namespaced settings from/to a specific section:
* - Core
* - Gui
* - Server
* - Plugins
* - Misc
*
* @note added in QGIS 3
*/
class CORE_EXPORT QgsSettings : public QObject
{
Q_OBJECT
public:
//! Sections for namespaced settings
enum Section
{
NoSection,
Core,
Gui,
Server,
Plugins,
Misc
};
/** Construct a QgsSettings object for accessing settings of the application
* called application from the organization called organization, and with parent parent.
*/
explicit QgsSettings( const QString &organization,
const QString &application = QString(), QObject *parent = nullptr );
/** Construct a QgsSettings object for accessing settings of the application called application
* from the organization called organization, and with parent parent.
* If scope is QSettings::UserScope, the QSettings object searches user-specific settings first,
* before it searches system-wide settings as a fallback. If scope is QSettings::SystemScope,
* the QSettings object ignores user-specific settings and provides access to system-wide settings.
*
* The storage format is set to QSettings::NativeFormat (i.e. calling setDefaultFormat() before
* calling this constructor has no effect).
*
* If no application name is given, the QSettings object will only access the organization-wide
* locations.
*/
QgsSettings( QSettings::Scope scope, const QString &organization,
const QString &application = QString(), QObject *parent = nullptr );
/** Construct a QgsSettings object for accessing settings of the application called application
* from the organization called organization, and with parent parent.
* If scope is QSettings::UserScope, the QSettings object searches user-specific settings first,
* before it searches system-wide settings as a fallback. If scope is QSettings::SystemScope,
* the QSettings object ignores user-specific settings and provides access to system-wide settings.
* If format is QSettings::NativeFormat, the native API is used for storing settings. If format
* is QSettings::IniFormat, the INI format is used.
*
* If no application name is given, the QSettings object will only access the organization-wide
* locations.
*/
QgsSettings( QSettings::Format format, QSettings::Scope scope, const QString &organization,
const QString &application = QString(), QObject *parent = nullptr );
/** Construct a QgsSettings object for accessing the settings stored in the file called fileName,
* with parent parent. If the file doesn't already exist, it is created.
*
* If format is QSettings::NativeFormat, the meaning of fileName depends on the platform. On Unix,
* fileName is the name of an INI file. On macOS and iOS, fileName is the name of a .plist file.
* On Windows, fileName is a path in the system registry.
*
* If format is QSettings::IniFormat, fileName is the name of an INI file.
*
* Warning: This function is provided for convenience. It works well for accessing INI or .plist
* files generated by Qt, but might fail on some syntaxes found in such files originated by
* other programs. In particular, be aware of the following limitations:
* - QgsSettings provides no way of reading INI "path" entries, i.e., entries with unescaped slash characters.
* (This is because these entries are ambiguous and cannot be resolved automatically.)
* - In INI files, QSettings uses the @ character as a metacharacter in some contexts, to encode
* Qt-specific data types (e.g., \@Rect), and might therefore misinterpret it when it occurs
* in pure INI files.
*/
QgsSettings( const QString &fileName, QSettings::Format format, QObject *parent = nullptr );
/** Constructs a QgsSettings object for accessing settings of the application and organization
* set previously with a call to QCoreApplication::setOrganizationName(),
* QCoreApplication::setOrganizationDomain(), and QCoreApplication::setApplicationName().
*
* The scope is QSettings::UserScope and the format is defaultFormat() (QSettings::NativeFormat
* by default). Use setDefaultFormat() before calling this constructor to change the default
* format used by this constructor.
*/
explicit QgsSettings( QObject *parent = 0 );
~QgsSettings();
/** Appends prefix to the current group.
* The current group is automatically prepended to all keys specified to QSettings.
* In addition, query functions such as childGroups(), childKeys(), and allKeys()
* are based on the group. By default, no group is set.
*/
void beginGroup( const QString &prefix );
//! Resets the group to what it was before the corresponding beginGroup() call.
void endGroup();
//! Returns a list of all keys, including subkeys, that can be read using the QSettings object.
QStringList allKeys() const;
//! Returns a list of all top-level keys that can be read using the QSettings object.
QStringList childKeys() const;
//! Returns a list of all key top-level groups that contain keys that can be read using the QSettings object.
QStringList childGroups() const;
//! Return the path to the Global Settings QSettings storage file
static QString globalSettingsPath() { return sGlobalSettingsPath; }
//! Set the Global Settings QSettings storage file
static bool setGlobalSettingsPath( QString path );
//! Adds prefix to the current group and starts reading from an array. Returns the size of the array.
int beginReadArray( const QString &prefix );
//! Closes the array that was started using beginReadArray() or beginWriteArray().
void endArray();
//! Sets the current array index to i. Calls to functions such as setValue(), value(),
//! remove(), and contains() will operate on the array entry at that index.
void setArrayIndex( int i );
//! Sets the value of setting key to value. If the key already exists, the previous value is overwritten.
//! An optional Section argument can be used to set a value to a specific Section.
//! @note keys are case insensitive
void setValue( const QString &key, const QVariant &value, const Section section = Section::NoSection );
/** Returns the value for setting key. If the setting doesn't exist, it will be
* searched in the Global Settings and if not found, returns defaultValue.
* If no default value is specified, a default QVariant is returned.
* An optional Section argument can be used to get a value from a specific Section.
*/
QVariant value( const QString &key, const QVariant &defaultValue = QVariant(),
const Section section = Section::NoSection ) const;
//! Returns true if there exists a setting called key; returns false otherwise.
//! If a group is set using beginGroup(), key is taken to be relative to that group.
bool contains( const QString &key ) const;
//! Returns the path where settings written using this QSettings object are stored.
QString fileName() const;
//! Writes any unsaved changes to permanent storage, and reloads any settings that have been
//! changed in the meantime by another application.
//! This function is called automatically from QSettings's destructor and by the event
//! loop at regular intervals, so you normally don't need to call it yourself.
void sync();
//! Removes the setting key and any sub-settings of key.
void remove( const QString &key );
//! Return the sanitized and prefixed key
QString prefixedKey( const QString &key, const Section section ) const;
private:
static QString sGlobalSettingsPath;
void init( );
QString sanitizeKey( QString key ) const;
QSettings* mUserSettings = nullptr;
QSettings* mGlobalSettings = nullptr;
bool mUsingGlobalArray = false;
Q_DISABLE_COPY( QgsSettings )
};
#endif // QGSSETTINGS_H

View File

@ -26,11 +26,11 @@
#include "qgsproviderregistry.h"
#include "qgswmsconnection.h"
#include "qgsnetworkaccessmanager.h"
#include "qgssettings.h"
#include <QInputDialog>
#include <QMessageBox>
#include <QPicture>
#include <QSettings>
#include <QUrl>
#include <QNetworkRequest>
@ -41,7 +41,7 @@ QgsWMSConnection::QgsWMSConnection( const QString& connName )
{
QgsDebugMsg( "theConnName = " + connName );
QSettings settings;
QgsSettings settings;
QString key = "/Qgis/connections-wms/" + mConnName;
QString credentialsKey = "/Qgis/WMS/" + mConnName;
@ -123,26 +123,26 @@ QgsDataSourceUri QgsWMSConnection::uri()
QStringList QgsWMSConnection::connectionList()
{
QSettings settings;
QgsSettings settings;
settings.beginGroup( QStringLiteral( "/Qgis/connections-wms" ) );
return settings.childGroups();
}
QString QgsWMSConnection::selectedConnection()
{
QSettings settings;
QgsSettings settings;
return settings.value( QStringLiteral( "/Qgis/connections-wms/selected" ) ).toString();
}
void QgsWMSConnection::setSelectedConnection( const QString& name )
{
QSettings settings;
QgsSettings settings;
settings.setValue( QStringLiteral( "/Qgis/connections-wms/selected" ), name );
}
void QgsWMSConnection::deleteConnection( const QString& name )
{
QSettings settings;
QgsSettings settings;
settings.remove( "/Qgis/connections-wms/" + name );
settings.remove( "/Qgis/WMS/" + name );
}

View File

@ -145,6 +145,7 @@ ADD_PYTHON_TEST(PyQgsLayerDependencies test_layer_dependencies.py)
ADD_PYTHON_TEST(PyQgsVersionCompare test_versioncompare.py)
ADD_PYTHON_TEST(PyQgsDBManagerGpkg test_db_manager_gpkg.py)
ADD_PYTHON_TEST(PyQgsFileDownloader test_qgsfiledownloader.py)
ADD_PYTHON_TEST(PyQgsSettings test_qgssettings.py)
IF (NOT WIN32)
ADD_PYTHON_TEST(PyQgsLogger test_qgslogger.py)

View File

@ -0,0 +1,223 @@
# -*- coding: utf-8 -*-
"""
Test the QgsSettings class
Run with: ctest -V -R PyQgsSettings
.. note:: This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
"""
from __future__ import print_function
from future import standard_library
import os
import tempfile
from qgis.core import (QgsSettings,)
from qgis.testing import start_app, unittest
from qgis.PyQt.QtCore import QSettings
standard_library.install_aliases()
__author__ = 'Alessandro Pasotti'
__date__ = '02/02/2017'
__copyright__ = 'Copyright 2017, The QGIS Project'
# This will get replaced with a git SHA1 when you do a git archive
__revision__ = '$Format:%H$'
start_app()
class TestQgsSettings(unittest.TestCase):
cnt = 0
def setUp(self):
self.cnt += 1
h, path = tempfile.mkstemp('.ini')
assert QgsSettings.setGlobalSettingsPath(path)
self.settings = QgsSettings('testqgissettings', 'testqgissettings%s' % self.cnt)
self.globalsettings = QSettings(self.settings.globalSettingsPath(), QSettings.IniFormat)
def tearDown(self):
settings_file = self.settings.fileName()
settings_default_file = self.settings.globalSettingsPath()
del(self.settings)
try:
os.unlink(settings_file)
except:
pass
try:
os.unlink(settings_default_file)
except:
pass
def addToDefaults(self, key, value):
self.globalsettings.setValue(key, value)
self.globalsettings.sync()
def addArrayToDefaults(self, prefix, key, values):
defaults = QSettings(self.settings.globalSettingsPath(), QSettings.IniFormat)
self.globalsettings.beginWriteArray(prefix)
i = 0
for v in values:
self.globalsettings.setArrayIndex(i)
self.globalsettings.setValue(key, v)
i += 1
self.globalsettings.endArray()
self.globalsettings.sync()
def test_basic_functionality(self):
self.assertEqual(self.settings.value('testqgissettings/doesnotexists', 'notexist'), 'notexist')
self.settings.setValue('testqgissettings/name', 'qgisrocks')
self.settings.sync()
self.assertEqual(self.settings.value('testqgissettings/name'), 'qgisrocks')
def test_defaults(self):
self.assertIsNone(self.settings.value('testqgissettings/name'))
self.addToDefaults('testqgissettings/name', 'qgisrocks')
self.assertEqual(self.settings.value('testqgissettings/name'), 'qgisrocks')
def test_allkeys(self):
self.assertEqual(self.settings.allKeys(), [])
self.addToDefaults('testqgissettings/name', 'qgisrocks')
self.addToDefaults('testqgissettings/name2', 'qgisrocks2')
self.settings.setValue('nepoti/eman', 'osaple')
self.assertEqual(3, len(self.settings.allKeys()))
self.assertIn('testqgissettings/name', self.settings.allKeys())
self.assertIn('nepoti/eman', self.settings.allKeys())
self.assertEqual('qgisrocks', self.settings.value('testqgissettings/name'))
self.assertEqual('qgisrocks2', self.settings.value('testqgissettings/name2'))
self.assertEqual('qgisrocks', self.globalsettings.value('testqgissettings/name'))
self.assertEqual('osaple', self.settings.value('nepoti/eman'))
self.assertEqual(3, len(self.settings.allKeys()))
self.assertEqual(2, len(self.globalsettings.allKeys()))
def test_precedence(self):
self.assertEqual(self.settings.allKeys(), [])
self.addToDefaults('testqgissettings/names/name1', 'qgisrocks1')
self.settings.setValue('testqgissettings/names/name1', 'qgisrocks-1')
self.assertEqual(self.settings.value('testqgissettings/names/name1'), 'qgisrocks-1')
def test_uft8(self):
self.assertEqual(self.settings.allKeys(), [])
self.addToDefaults('testqgissettings/names/namèé↓1', 'qgisrocks↓1')
self.assertEqual(self.settings.value('testqgissettings/names/namèé↓1'), 'qgisrocks↓1')
self.settings.setValue('testqgissettings/names/namèé↓2', 'qgisrocks↓2')
self.assertEqual(self.settings.value('testqgissettings/names/namèé↓2'), 'qgisrocks↓2')
self.settings.setValue('testqgissettings/names/namèé↓1', 'qgisrocks↓-1')
self.assertEqual(self.settings.value('testqgissettings/names/namèé↓1'), 'qgisrocks↓-1')
def test_groups(self):
self.assertEqual(self.settings.allKeys(), [])
self.addToDefaults('testqgissettings/names/name1', 'qgisrocks1')
self.addToDefaults('testqgissettings/names/name2', 'qgisrocks2')
self.addToDefaults('testqgissettings/names/name3', 'qgisrocks3')
self.addToDefaults('testqgissettings/name', 'qgisrocks')
self.settings.beginGroup('testqgissettings')
self.assertEqual(['names'], self.settings.childGroups())
self.settings.setValue('surnames/name1', 'qgisrocks-1')
self.assertEqual(['surnames', 'names'], self.settings.childGroups())
self.settings.setValue('names/name1', 'qgisrocks-1')
self.assertEqual('qgisrocks-1', self.settings.value('names/name1'))
self.settings.endGroup()
self.settings.beginGroup('testqgissettings/names')
self.settings.setValue('name4', 'qgisrocks-4')
keys = list(self.settings.childKeys())
keys.sort()
self.assertEqual(keys, ['name1', 'name2', 'name3', 'name4'])
self.settings.endGroup()
self.assertEqual('qgisrocks-1', self.settings.value('testqgissettings/names/name1'))
self.assertEqual('qgisrocks-4', self.settings.value('testqgissettings/names/name4'))
def test_array(self):
self.assertEqual(self.settings.allKeys(), [])
self.addArrayToDefaults('testqgissettings', 'key', ['qgisrocks1', 'qgisrocks2', 'qgisrocks3'])
self.assertEqual(self.settings.allKeys(), ['testqgissettings/1/key', 'testqgissettings/2/key', 'testqgissettings/3/key', 'testqgissettings/size'])
self.assertEqual(self.globalsettings.allKeys(), ['testqgissettings/1/key', 'testqgissettings/2/key', 'testqgissettings/3/key', 'testqgissettings/size'])
self.assertEqual(3, self.globalsettings.beginReadArray('testqgissettings'))
self.globalsettings.endArray()
self.assertEqual(3, self.settings.beginReadArray('testqgissettings'))
values = []
for i in range(3):
self.settings.setArrayIndex(i)
values.append(self.settings.value("key"))
self.assertEqual(values, ['qgisrocks1', 'qgisrocks2', 'qgisrocks3'])
def test_section_getters_setters(self):
self.assertEqual(self.settings.allKeys(), [])
self.settings.setValue('key1', 'core1', QgsSettings.Core)
self.settings.setValue('key2', 'core2', QgsSettings.Core)
self.settings.setValue('key1', 'server1', QgsSettings.Server)
self.settings.setValue('key2', 'server2', QgsSettings.Server)
self.settings.setValue('key1', 'gui1', QgsSettings.Gui)
self.settings.setValue('key2', 'gui2', QgsSettings.Gui)
self.settings.setValue('key1', 'plugins1', QgsSettings.Plugins)
self.settings.setValue('key2', 'plugins2', QgsSettings.Plugins)
self.settings.setValue('key1', 'misc1', QgsSettings.Misc)
self.settings.setValue('key2', 'misc2', QgsSettings.Misc)
# Test that the values are namespaced
self.assertEqual(self.settings.value('core/key1'), 'core1')
self.assertEqual(self.settings.value('core/key2'), 'core2')
self.assertEqual(self.settings.value('server/key1'), 'server1')
self.assertEqual(self.settings.value('server/key2'), 'server2')
self.assertEqual(self.settings.value('gui/key1'), 'gui1')
self.assertEqual(self.settings.value('gui/key2'), 'gui2')
self.assertEqual(self.settings.value('plugins/key1'), 'plugins1')
self.assertEqual(self.settings.value('plugins/key2'), 'plugins2')
self.assertEqual(self.settings.value('misc/key1'), 'misc1')
self.assertEqual(self.settings.value('misc/key2'), 'misc2')
# Test getters
self.assertEqual(self.settings.value('key1', None, QgsSettings.Core), 'core1')
self.assertEqual(self.settings.value('key2', None, QgsSettings.Core), 'core2')
self.assertEqual(self.settings.value('key1', None, QgsSettings.Server), 'server1')
self.assertEqual(self.settings.value('key2', None, QgsSettings.Server), 'server2')
self.assertEqual(self.settings.value('key1', None, QgsSettings.Gui), 'gui1')
self.assertEqual(self.settings.value('key2', None, QgsSettings.Gui), 'gui2')
self.assertEqual(self.settings.value('key1', None, QgsSettings.Plugins), 'plugins1')
self.assertEqual(self.settings.value('key2', None, QgsSettings.Plugins), 'plugins2')
self.assertEqual(self.settings.value('key1', None, QgsSettings.Misc), 'misc1')
self.assertEqual(self.settings.value('key2', None, QgsSettings.Misc), 'misc2')
# Test default values on Section getter
self.assertEqual(self.settings.value('key_not_exist', 'misc_not_exist', QgsSettings.Misc), 'misc_not_exist')
def test_contains(self):
self.assertEqual(self.settings.allKeys(), [])
self.addToDefaults('testqgissettings/name', 'qgisrocks1')
self.addToDefaults('testqgissettings/name2', 'qgisrocks2')
self.assertTrue(self.settings.contains('testqgissettings/name'))
self.assertTrue(self.settings.contains('testqgissettings/name2'))
self.settings.setValue('testqgissettings/name3', 'qgisrocks3')
self.assertTrue(self.settings.contains('testqgissettings/name3'))
if __name__ == '__main__':
unittest.main()