1 /*************************************************************************** 2 qgssettings.h 3 -------------------------------------- 4 Date : January 2017 5 Copyright : (C) 2017 by Alessandro Pasotti 6 Email : apasotti at boundlessgeo dot com 7 *************************************************************************** 8 * * 9 * This program is free software; you can redistribute it and/or modify * 10 * it under the terms of the GNU General Public License as published by * 11 * the Free Software Foundation; either version 2 of the License, or * 12 * (at your option) any later version. * 13 * * 14 ***************************************************************************/ 15 16 17 #ifndef QGSSETTINGS_H 18 #define QGSSETTINGS_H 19 20 #include <QSettings> 21 #include <QMetaEnum> 22 23 #include "qgis_core.h" 24 #include "qgis_sip.h" 25 #include "qgslogger.h" 26 27 /** 28 * \ingroup core 29 * \class QgsSettings 30 * 31 * \brief This class is a composition of two QSettings instances: 32 * 33 * - the main QSettings instance is the standard User Settings and 34 * - the second one (Global Settings) is meant to provide read-only 35 * pre-configuration and defaults to the first one. 36 * 37 * For a given settings key, the function call to value(key, default) will return 38 * the first existing setting in the order specified below: 39 * 40 * - User Settings 41 * - Global Settings 42 * - Default Value 43 * 44 * The path to the Global Settings storage can be set before constructing the QgsSettings 45 * objects, with a static call to: 46 * static bool setGlobalSettingsPath( QString path ); 47 * 48 * QgsSettings provides some shortcuts to get/set namespaced settings from/to a specific section: 49 * 50 * - Core 51 * - Gui 52 * - Server 53 * - Plugins 54 * - Auth 55 * - App 56 * - Providers 57 * - Misc 58 * 59 * \since QGIS 3.0 60 */ 61 class CORE_EXPORT QgsSettings : public QObject 62 { 63 Q_OBJECT 64 public: 65 66 //! Sections for namespaced settings 67 enum Section 68 { 69 NoSection, 70 Core, 71 Gui, 72 Server, 73 Plugins, 74 Auth, 75 App, 76 Providers, 77 Expressions, 78 Misc, 79 Gps, //!< GPS section, since QGIS 3.22 80 }; 81 82 /** 83 * Constructs a QgsSettings object for accessing settings of the application 84 * called application from the organization called organization, and with parent parent. 85 */ 86 explicit QgsSettings( const QString &organization, 87 const QString &application = QString(), QObject *parent = nullptr ); 88 89 /** 90 * Constructs a QgsSettings object for accessing settings of the application called application 91 * from the organization called organization, and with parent parent. 92 * 93 * If scope is QSettings::UserScope, the QSettings object searches user-specific settings first, 94 * before it searches system-wide settings as a fallback. If scope is QSettings::SystemScope, 95 * the QSettings object ignores user-specific settings and provides access to system-wide settings. 96 * 97 * The storage format is set to QSettings::NativeFormat (i.e. calling setDefaultFormat() before 98 * calling this constructor has no effect). 99 * 100 * If no application name is given, the QSettings object will only access the organization-wide 101 * locations. 102 */ 103 QgsSettings( QSettings::Scope scope, const QString &organization, 104 const QString &application = QString(), QObject *parent = nullptr ); 105 106 /** 107 * Constructs a QgsSettings object for accessing settings of the application called application 108 * from the organization called organization, and with parent parent. 109 * 110 * If scope is QSettings::UserScope, the QSettings object searches user-specific settings first, 111 * before it searches system-wide settings as a fallback. If scope is QSettings::SystemScope, 112 * the QSettings object ignores user-specific settings and provides access to system-wide settings. 113 * 114 * If format is QSettings::NativeFormat, the native API is used for storing settings. If format 115 * is QSettings::IniFormat, the INI format is used. 116 * 117 * If no application name is given, the QSettings object will only access the organization-wide 118 * locations. 119 */ 120 QgsSettings( QSettings::Format format, QSettings::Scope scope, const QString &organization, 121 const QString &application = QString(), QObject *parent = nullptr ); 122 123 /** 124 * Constructs a QgsSettings object for accessing the settings stored in the file called fileName, 125 * with parent parent. If the file doesn't already exist, it is created. 126 * 127 * If format is QSettings::NativeFormat, the meaning of fileName depends on the platform. On Unix, 128 * fileName is the name of an INI file. On macOS and iOS, fileName is the name of a .plist file. 129 * On Windows, fileName is a path in the system registry. 130 * 131 * If format is QSettings::IniFormat, fileName is the name of an INI file. 132 * 133 * \warning This function is provided for convenience. It works well for accessing INI or .plist 134 * files generated by Qt, but might fail on some syntaxes found in such files originated by 135 * other programs. In particular, be aware of the following limitations: 136 * 137 * - QgsSettings provides no way of reading INI "path" entries, i.e., entries with unescaped slash characters. 138 * (This is because these entries are ambiguous and cannot be resolved automatically.) 139 * - In INI files, QSettings uses the @ character as a metacharacter in some contexts, to encode 140 * Qt-specific data types (e.g., \@Rect), and might therefore misinterpret it when it occurs 141 * in pure INI files. 142 */ 143 QgsSettings( const QString &fileName, QSettings::Format format, QObject *parent = nullptr ); 144 145 /** 146 * Constructs a QgsSettings object for accessing settings of the application and organization 147 * set previously with a call to QCoreApplication::setOrganizationName(), 148 * QCoreApplication::setOrganizationDomain(), and QCoreApplication::setApplicationName(). 149 * 150 * The scope is QSettings::UserScope and the format is defaultFormat() (QSettings::NativeFormat 151 * by default). Use setDefaultFormat() before calling this constructor to change the default 152 * format used by this constructor. 153 */ 154 explicit QgsSettings( QObject *parent = nullptr ); 155 ~QgsSettings() override; 156 157 /** 158 * Appends prefix to the current group. 159 * The current group is automatically prepended to all keys specified to QSettings. 160 * In addition, query functions such as childGroups(), childKeys(), and allKeys() 161 * are based on the group. By default, no group is set. 162 */ 163 void beginGroup( const QString &prefix, QgsSettings::Section section = QgsSettings::NoSection ); 164 //! Resets the group to what it was before the corresponding beginGroup() call. 165 void endGroup(); 166 167 /** 168 * Returns the current group. 169 * \see beginGroup() 170 * \see endGroup() 171 * \since QGIS 3.6 172 */ 173 QString group() const; 174 175 //! Returns a list of all keys, including subkeys, that can be read using the QSettings object. 176 QStringList allKeys() const; 177 //! Returns a list of all top-level keys that can be read using the QSettings object. 178 QStringList childKeys() const; 179 //! Returns a list of all key top-level groups that contain keys that can be read using the QSettings object. 180 QStringList childGroups() const; 181 //! Returns a list of all key top-level groups (same as childGroups) but only for groups defined in global settings. 182 QStringList globalChildGroups() const; 183 //! Returns the path to the Global Settings QSettings storage file 184 static QString globalSettingsPath(); 185 //! Sets the Global Settings QSettings storage file 186 static bool setGlobalSettingsPath( const QString &path ); 187 //! Adds prefix to the current group and starts reading from an array. Returns the size of the array. 188 int beginReadArray( const QString &prefix ); 189 190 /** 191 * Adds prefix to the current group and starts writing an array of size size. 192 * If size is -1 (the default), it is automatically determined based on the indexes of the entries written. 193 * \note This will completely shadow any existing array with the same name in the global settings 194 */ 195 void beginWriteArray( const QString &prefix, int size = -1 ); 196 //! Closes the array that was started using beginReadArray() or beginWriteArray(). 197 void endArray(); 198 199 /** 200 * Sets the current array index to i. Calls to functions such as setValue(), value(), 201 * remove(), and contains() will operate on the array entry at that index. 202 */ 203 void setArrayIndex( int i ); 204 205 /** 206 * Sets the value of setting key to value. If the key already exists, the previous value is overwritten. 207 * An optional Section argument can be used to set a value to a specific Section. 208 */ 209 void setValue( const QString &key, const QVariant &value, QgsSettings::Section section = QgsSettings::NoSection ); 210 211 /** 212 * Returns the value for setting key. If the setting doesn't exist, it will be 213 * searched in the Global Settings and if not found, returns defaultValue. 214 * If no default value is specified, a default QVariant is returned. 215 * An optional Section argument can be used to get a value from a specific Section. 216 */ 217 #ifndef SIP_RUN 218 QVariant value( const QString &key, const QVariant &defaultValue = QVariant(), 219 Section section = NoSection ) const; 220 #else 221 SIP_PYOBJECT value( const QString &key, const QVariant &defaultValue = QVariant(), 222 SIP_PYOBJECT type = 0, 223 QgsSettings::Section section = QgsSettings::NoSection ) const / ReleaseGIL /; 224 % MethodCode 225 typedef PyObject *( *pyqt5_from_qvariant_by_type )( QVariant &value, PyObject *type ); 226 QVariant value; 227 228 // QSettings has an internal mutex so release the GIL to avoid the possibility of deadlocks. 229 Py_BEGIN_ALLOW_THREADS 230 value = sipCpp->value( *a0, *a1, a3 ); 231 Py_END_ALLOW_THREADS 232 233 pyqt5_from_qvariant_by_type f = ( pyqt5_from_qvariant_by_type ) sipImportSymbol( "pyqt5_from_qvariant_by_type" ); 234 sipRes = f( value, a2 ); 235 236 sipIsErr = !sipRes; 237 % End 238 #endif 239 240 #ifndef SIP_RUN 241 242 /** 243 * Returns the setting value for a setting based on an enum. 244 * This forces the output to be a valid and existing entry of the enum. 245 * Hence if the setting value is incorrect, the given default value is returned. 246 * This tries first with setting as a string (as the enum) and then as an integer value. 247 * \note The enum needs to be declared with Q_ENUM, and flags with Q_FLAG (not Q_FLAGS). 248 * \note for Python bindings, a custom implementation is achieved in Python directly 249 * \see setEnumValue 250 * \see flagValue 251 */ 252 template <class T> 253 T enumValue( const QString &key, const T &defaultValue, 254 const Section section = NoSection ) 255 { 256 const QMetaEnum metaEnum = QMetaEnum::fromType<T>(); 257 Q_ASSERT( metaEnum.isValid() ); 258 if ( !metaEnum.isValid() ) 259 { 260 QgsDebugMsg( QStringLiteral( "Invalid metaenum. Enum probably misses Q_ENUM or Q_FLAG declaration." ) ); 261 } 262 263 T v; 264 bool ok = false; 265 266 if ( metaEnum.isValid() ) 267 { 268 // read as string 269 QByteArray ba = value( key, metaEnum.valueToKey( static_cast<const int>( defaultValue ) ), section ).toString().toUtf8(); 270 const char *vs = ba.data(); 271 v = static_cast<T>( metaEnum.keyToValue( vs, &ok ) ); 272 if ( ok ) 273 return v; 274 } 275 276 // if failed, try to read as int (old behavior) 277 // this code shall be removed later (probably after QGIS 3.4 LTR for 3.6) 278 // then the method could be marked as const 279 v = static_cast<T>( value( key, static_cast<const int>( defaultValue ), section ).toInt( &ok ) ); 280 if ( metaEnum.isValid() ) 281 { 282 if ( !ok || !metaEnum.valueToKey( static_cast<int>( v ) ) ) 283 { 284 v = defaultValue; 285 } 286 else 287 { 288 // found setting as an integer 289 // convert the setting to the new form (string) 290 setEnumValue( key, v, section ); 291 } 292 } 293 294 return v; 295 } 296 297 /** 298 * Set the value of a setting based on an enum. 299 * The setting will be saved as string. 300 * \note The enum needs to be declared with Q_ENUM, and flags with Q_FLAG (not Q_FLAGS). 301 * \see enumValue 302 * \see setFlagValue 303 */ 304 template <class T> 305 void setEnumValue( const QString &key, const T &value, 306 const Section section = NoSection ) 307 { 308 const QMetaEnum metaEnum = QMetaEnum::fromType<T>(); 309 Q_ASSERT( metaEnum.isValid() ); 310 if ( metaEnum.isValid() ) 311 { 312 setValue( key, metaEnum.valueToKey( static_cast<const int>( value ) ), section ); 313 } 314 else 315 { 316 QgsDebugMsg( QStringLiteral( "Invalid metaenum. Enum probably misses Q_ENUM or Q_FLAG declaration." ) ); 317 } 318 } 319 320 /** 321 * Returns the setting value for a setting based on a flag. 322 * This forces the output to be a valid and existing entry of the flag. 323 * Hence if the setting value is incorrect, the given default value is returned. 324 * This tries first with setting as a string (using a byte array) and then as an integer value. 325 * \note The flag needs to be declared with Q_FLAG (not Q_FLAGS). 326 * \note for Python bindings, a custom implementation is achieved in Python directly. 327 * \see setFlagValue 328 * \see enumValue 329 */ 330 template <class T> 331 T flagValue( const QString &key, const T &defaultValue, 332 const Section section = NoSection ) 333 { 334 const QMetaEnum metaEnum = QMetaEnum::fromType<T>(); 335 Q_ASSERT( metaEnum.isValid() ); 336 if ( !metaEnum.isValid() ) 337 { 338 QgsDebugMsg( QStringLiteral( "Invalid metaenum. Enum probably misses Q_ENUM or Q_FLAG declaration." ) ); 339 } 340 341 T v = defaultValue; 342 bool ok = false; 343 344 if ( metaEnum.isValid() ) 345 { 346 // read as string 347 QByteArray ba = value( key, metaEnum.valueToKeys( static_cast< const int >( defaultValue ) ) ).toString().toUtf8(); 348 const char *vs = ba.data(); 349 v = static_cast<T>( metaEnum.keysToValue( vs, &ok ) ); 350 } 351 if ( !ok ) 352 { 353 // if failed, try to read as int 354 const int intValue = value( key, static_cast<const int>( defaultValue ), section ).toInt( &ok ); 355 if ( metaEnum.isValid() ) 356 { 357 if ( ok ) 358 { 359 // check that the int value does correspond to a flag 360 // see https://stackoverflow.com/a/68495949/1548052 361 const QByteArray keys = metaEnum.valueToKeys( intValue ); 362 const int intValueCheck = metaEnum.keysToValue( keys ); 363 if ( intValue != intValueCheck ) 364 { 365 v = defaultValue; 366 } 367 else 368 { 369 // found property as an integer 370 v = T( intValue ); 371 // convert the property to the new form (string) 372 // this code could be removed 373 // then the method could be marked as const 374 setFlagValue( key, v ); 375 } 376 } 377 else 378 { 379 v = defaultValue; 380 } 381 } 382 } 383 384 return v; 385 } 386 387 /** 388 * Set the value of a setting based on a flag. 389 * The setting will be saved as string. 390 * \note The flag needs to be declared with Q_FLAG (not Q_FLAGS). 391 * \see flagValue 392 * \see setEnumValue 393 */ 394 template <class T> 395 void setFlagValue( const QString &key, const T &value, 396 const Section section = NoSection ) 397 { 398 const QMetaEnum metaEnum = QMetaEnum::fromType<T>(); 399 Q_ASSERT( metaEnum.isValid() ); 400 if ( metaEnum.isValid() ) 401 { 402 setValue( key, metaEnum.valueToKeys( static_cast< const int >( value ) ), section ); 403 } 404 else 405 { 406 QgsDebugMsg( QStringLiteral( "Invalid metaenum. Enum probably misses Q_ENUM or Q_FLAG declaration." ) ); 407 } 408 } 409 #endif 410 411 /** 412 * Returns TRUE if there exists a setting called key; returns FALSE otherwise. 413 * If a group is set using beginGroup(), key is taken to be relative to that group. 414 */ 415 bool contains( const QString &key, QgsSettings::Section section = QgsSettings::NoSection ) const; 416 //! Returns the path where settings written using this QSettings object are stored. 417 QString fileName() const; 418 419 /** 420 * Writes any unsaved changes to permanent storage, and reloads any settings that have been 421 * changed in the meantime by another application. 422 * This function is called automatically from QSettings's destructor and by the event 423 * loop at regular intervals, so you normally don't need to call it yourself. 424 */ 425 void sync(); 426 //! Removes the setting key and any sub-settings of key in a section. 427 void remove( const QString &key, QgsSettings::Section section = QgsSettings::NoSection ); 428 //! Returns the sanitized and prefixed key 429 QString prefixedKey( const QString &key, QgsSettings::Section section ) const; 430 //! Removes all entries in the user settings 431 void clear(); 432 433 private: 434 void init(); 435 QString sanitizeKey( const QString &key ) const; 436 QSettings *mUserSettings = nullptr; 437 QSettings *mGlobalSettings = nullptr; 438 bool mUsingGlobalArray = false; 439 Q_DISABLE_COPY( QgsSettings ) 440 441 }; 442 443 #endif // QGSSETTINGS_H 444