Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qqmlsettings.cpp
Go to the documentation of this file.
1// Copyright (C) 2016 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3// Qt-Security score:significant reason:default
4
6#include <qcoreevent.h>
7#include <qcoreapplication.h>
8#include <qloggingcategory.h>
9#include <qsettings.h>
10#include <qpointer.h>
11#include <qjsvalue.h>
12#include <qqmlinfo.h>
13#include <qdebug.h>
14#include <qhash.h>
15
17
18/*!
19 \qmlmodule Qt.labs.settings 1.0
20 \title Qt Labs Settings QML Types
21 \ingroup qmlmodules
22 \deprecated [6.5] Use \l [QML] {QtCore::}{Settings} from Qt Qml Core instead.
23 \brief Provides persistent platform-independent application settings.
24
25 To use this module, import the module with the following line:
26
27 \code
28 import Qt.labs.settings
29 \endcode
30*/
31
32/*!
33 \qmltype Settings
34//! \nativetype QQmlSettingsLabs
35 \inqmlmodule Qt.labs.settings
36 \ingroup settings
37 \deprecated [6.5] Use \l [QML] {QtCore::}{Settings} from Qt Qml Core instead.
38 \brief Provides persistent platform-independent application settings.
39
40 The Settings type provides persistent platform-independent application settings.
41
42 \note This type is made available by importing the \b Qt.labs.settings module.
43 \e {Types in the Qt.labs module are not guaranteed to remain compatible
44 in future versions.}
45
46 Users normally expect an application to remember its settings (window sizes
47 and positions, options, etc.) across sessions. The Settings type enables you
48 to save and restore such application settings with the minimum of effort.
49
50 Individual setting values are specified by declaring properties within a
51 Settings element. All \l {QML Value Types}{value type} properties are
52 supported. The recommended approach is to use property aliases in order
53 to get automatic property updates both ways. The following example shows
54 how to use Settings to store and restore the geometry of a window.
55
56 \qml
57 import QtQuick.Window
58 import Qt.labs.settings
59
60 Window {
61 id: window
62
63 width: 800
64 height: 600
65
66 Settings {
67 property alias x: window.x
68 property alias y: window.y
69 property alias width: window.width
70 property alias height: window.height
71 }
72 }
73 \endqml
74
75 At first application startup, the window gets default dimensions specified
76 as 800x600. Notice that no default position is specified - we let the window
77 manager handle that. Later when the window geometry changes, new values will
78 be automatically stored to the persistent settings. The second application
79 run will get initial values from the persistent settings, bringing the window
80 back to the previous position and size.
81
82 A fully declarative syntax, achieved by using property aliases, comes at the
83 cost of storing persistent settings whenever the values of aliased properties
84 change. Normal properties can be used to gain more fine-grained control over
85 storing the persistent settings. The following example illustrates how to save
86 a setting on component destruction.
87
88 \qml
89 import QtQuick
90 import Qt.labs.settings
91
92 Item {
93 id: page
94
95 state: settings.state
96
97 states: [
98 State {
99 name: "active"
100 // ...
101 },
102 State {
103 name: "inactive"
104 // ...
105 }
106 ]
107
108 Settings {
109 id: settings
110 property string state: "active"
111 }
112
113 Component.onDestruction: {
114 settings.state = page.state
115 }
116 }
117 \endqml
118
119 Notice how the default value is now specified in the persistent setting property,
120 and the actual property is bound to the setting in order to get the initial value
121 from the persistent settings.
122
123 \section1 Application Identifiers
124
125 Application specific settings are identified by providing application
126 \l {QCoreApplication::applicationName}{name},
127 \l {QCoreApplication::organizationName}{organization} and
128 \l {QCoreApplication::organizationDomain}{domain}, or by specifying
129 \l fileName.
130
131 \code
132 #include <QGuiApplication>
133 #include <QQmlApplicationEngine>
134
135 int main(int argc, char *argv[])
136 {
137 QGuiApplication app(argc, argv);
138 app.setOrganizationName("Some Company");
139 app.setOrganizationDomain("somecompany.com");
140 app.setApplicationName("Amazing Application");
141
142 QQmlApplicationEngine engine("main.qml");
143 return app.exec();
144 }
145 \endcode
146
147 These are typically specified in C++ in the beginning of \c main(),
148 but can also be controlled in QML via the following properties:
149 \list
150 \li \l {Qt::application}{Qt.application.name},
151 \li \l {Qt::application}{Qt.application.organization} and
152 \li \l {Qt::application}{Qt.application.domain}.
153 \endlist
154
155 \section1 Categories
156
157 Application settings may be divided into logical categories by specifying
158 a category name via the \l category property. Using logical categories not
159 only provides a cleaner settings structure, but also prevents possible
160 conflicts between setting keys.
161
162 If several categories are required, use several Settings objects, each with
163 their own category:
164
165 \qml
166 Item {
167 id: panel
168
169 visible: true
170
171 Settings {
172 category: "OutputPanel"
173 property alias visible: panel.visible
174 // ...
175 }
176
177 Settings {
178 category: "General"
179 property alias fontSize: fontSizeSpinBox.value
180 // ...
181 }
182 }
183 \endqml
184
185 Instead of ensuring that all settings in the application have unique names,
186 the settings can be divided into unique categories that may then contain
187 settings using the same names that are used in other categories - without
188 a conflict.
189
190 \section1 Notes
191
192 The current implementation is based on \l QSettings. This imposes certain
193 limitations, such as missing change notifications. Writing a setting value
194 using one instance of Settings does not update the value in another Settings
195 instance, even if they are referring to the same setting in the same category.
196
197 The information is stored in the system registry on Windows, and in XML
198 preferences files on \macos. On other Unix systems, in the absence of a
199 standard, INI text files are used. See \l QSettings documentation for
200 more details.
201
202 \sa {QtCore::}{Settings}, QSettings
203*/
204
205Q_STATIC_LOGGING_CATEGORY(lcSettings, "qt.labs.settings")
206
207static const int settingsWriteDelay = 500;
208
210{
211 Q_DECLARE_PUBLIC(QQmlSettingsLabs)
212
213public:
215
217
218 void init();
219 void reset();
220
221 void load();
222 void store();
223
225 QVariant readProperty(const QMetaProperty &property) const;
226
228 int timerId = 0;
229 bool initialized = false;
233 QHash<const char *, QVariant> changedProperties;
234};
235
236QQmlSettingsLabsPrivate::QQmlSettingsLabsPrivate() {}
237
239{
240 if (!settings) {
241 QQmlSettingsLabs *q = const_cast<QQmlSettingsLabs*>(q_func());
242 settings = fileName.isEmpty() ? new QSettings(q) : new QSettings(fileName, QSettings::IniFormat, q);
243 if (settings->status() != QSettings::NoError) {
244 // TODO: can't print out the enum due to the following error:
245 // error: C2666: 'QQmlInfo::operator <<': 15 overloads have similar conversions
246 qmlWarning(q) << "Failed to initialize QSettings instance. Status code is: " << int(settings->status());
247
248 if (settings->status() == QSettings::AccessError) {
249 QVector<QString> missingIdentifiers;
250 if (QCoreApplication::organizationName().isEmpty())
251 missingIdentifiers.append(QLatin1String("organizationName"));
252 if (QCoreApplication::organizationDomain().isEmpty())
253 missingIdentifiers.append(QLatin1String("organizationDomain"));
254 if (QCoreApplication::applicationName().isEmpty())
255 missingIdentifiers.append(QLatin1String("applicationName"));
256
257 if (!missingIdentifiers.isEmpty())
258 qmlWarning(q) << "The following application identifiers have not been set: " << missingIdentifiers;
259 }
260 return settings;
261 }
262
263 if (!category.isEmpty())
264 settings->beginGroup(category);
265 if (initialized)
266 q->d_func()->load();
267 }
268 return settings;
269}
270
272{
273 if (!initialized) {
274 qCDebug(lcSettings) << "QQmlSettingsLabs: stored at" << instance()->fileName();
275 load();
276 initialized = true;
277 }
278}
279
281{
282 if (initialized && settings && !changedProperties.isEmpty())
283 store();
284 delete settings;
285}
286
288{
289 Q_Q(QQmlSettingsLabs);
290 const QMetaObject *mo = q->metaObject();
291 const int offset = mo->propertyOffset();
292 const int count = mo->propertyCount();
293
294 // don't save built-in properties if there aren't any qml properties
295 if (offset == 1)
296 return;
297
298 for (int i = offset; i < count; ++i) {
299 QMetaProperty property = mo->property(i);
300 const QString propertyName = QString::fromUtf8(property.name());
301
302 const QVariant previousValue = readProperty(property);
303 const QVariant currentValue = instance()->value(propertyName,
304 previousValue);
305
306 if (!currentValue.isNull() && (!previousValue.isValid()
307 || (currentValue.canConvert(previousValue.metaType())
308 && previousValue != currentValue))) {
309 property.write(q, currentValue);
310 qCDebug(lcSettings) << "QQmlSettingsLabs: load" << property.name() << "setting:" << currentValue << "default:" << previousValue;
311 }
312
313 // ensure that a non-existent setting gets written
314 // even if the property wouldn't change later
315 if (!instance()->contains(propertyName))
317
318 // setup change notifications on first load
319 if (!initialized && property.hasNotifySignal()) {
320 static const int propertyChangedIndex = mo->indexOfSlot("_q_propertyChanged()");
321 QMetaObject::connect(q, property.notifySignalIndex(), q, propertyChangedIndex);
322 }
323 }
324}
325
327{
328 QHash<const char *, QVariant>::const_iterator it = changedProperties.constBegin();
329 while (it != changedProperties.constEnd()) {
330 instance()->setValue(QString::fromUtf8(it.key()), it.value());
331 qCDebug(lcSettings) << "QQmlSettingsLabs: store" << it.key() << ":" << it.value();
332 ++it;
333 }
334 changedProperties.clear();
335}
336
338{
339 Q_Q(QQmlSettingsLabs);
340 const QMetaObject *mo = q->metaObject();
341 const int offset = mo->propertyOffset();
342 const int count = mo->propertyCount();
343 for (int i = offset; i < count; ++i) {
344 const QMetaProperty &property = mo->property(i);
345 const QVariant value = readProperty(property);
346 changedProperties.insert(property.name(), value);
347 qCDebug(lcSettings) << "QQmlSettingsLabs: cache" << property.name() << ":" << value;
348 }
349 if (timerId != 0)
350 q->killTimer(timerId);
351 timerId = q->startTimer(settingsWriteDelay);
352}
353
354QVariant QQmlSettingsLabsPrivate::readProperty(const QMetaProperty &property) const
355{
356 Q_Q(const QQmlSettingsLabs);
357 QVariant var = property.read(q);
358 if (var.metaType() == QMetaType::fromType<QJSValue>())
359 var = var.value<QJSValue>().toVariant();
360 return var;
361}
362
363QQmlSettingsLabs::QQmlSettingsLabs(QObject *parent)
364 : QObject(parent), d_ptr(new QQmlSettingsLabsPrivate)
365{
366 Q_D(QQmlSettingsLabs);
367 d->q_ptr = this;
368}
369
370QQmlSettingsLabs::~QQmlSettingsLabs()
371{
372 Q_D(QQmlSettingsLabs);
373 d->reset(); // flush pending changes
374}
375
376/*!
377 \qmlproperty string Settings::category
378
379 This property holds the name of the settings category.
380
381 Categories can be used to group related settings together.
382*/
383QString QQmlSettingsLabs::category() const
384{
385 Q_D(const QQmlSettingsLabs);
386 return d->category;
387}
388
389void QQmlSettingsLabs::setCategory(const QString &category)
390{
391 Q_D(QQmlSettingsLabs);
392 if (d->category != category) {
393 d->reset();
394 d->category = category;
395 if (d->initialized)
396 d->load();
397 }
398}
399
400/*!
401 \qmlproperty string Settings::fileName
402
403 This property holds the path to the settings file. If the file doesn't
404 already exist, it is created.
405
406 \since Qt 5.12
407
408 \sa QSettings::fileName, QSettings::IniFormat
409*/
410QString QQmlSettingsLabs::fileName() const
411{
412 Q_D(const QQmlSettingsLabs);
413 return d->fileName;
414}
415
416void QQmlSettingsLabs::setFileName(const QString &fileName)
417{
418 Q_D(QQmlSettingsLabs);
419 if (d->fileName != fileName) {
420 d->reset();
421 d->fileName = fileName;
422 if (d->initialized)
423 d->load();
424 }
425}
426
427/*!
428 \qmlmethod var Settings::value(string key, var defaultValue)
429
430 Returns the value for setting \a key. If the setting doesn't exist,
431 returns \a defaultValue.
432
433 \since Qt 5.12
434
435 \sa QSettings::value
436*/
437QVariant QQmlSettingsLabs::value(const QString &key, const QVariant &defaultValue) const
438{
439 Q_D(const QQmlSettingsLabs);
440 return d->instance()->value(key, defaultValue);
441}
442
443/*!
444 \qmlmethod Settings::setValue(string key, var value)
445
446 Sets the value of setting \a key to \a value. If the key already exists,
447 the previous value is overwritten.
448
449 \since Qt 5.12
450
451 \sa QSettings::setValue
452*/
453void QQmlSettingsLabs::setValue(const QString &key, const QVariant &value)
454{
455 Q_D(const QQmlSettingsLabs);
456 d->instance()->setValue(key, value);
457 qCDebug(lcSettings) << "QQmlSettingsLabs: setValue" << key << ":" << value;
458}
459
460/*!
461 \qmlmethod Settings::sync()
462
463 Writes any unsaved changes to permanent storage, and reloads any
464 settings that have been changed in the meantime by another
465 application.
466
467 This function is called automatically from QSettings's destructor and
468 by the event loop at regular intervals, so you normally don't need to
469 call it yourself.
470
471 \sa QSettings::sync
472*/
473void QQmlSettingsLabs::sync()
474{
475 Q_D(QQmlSettingsLabs);
476 d->instance()->sync();
477}
478
479void QQmlSettingsLabs::classBegin()
480{
481}
482
483void QQmlSettingsLabs::componentComplete()
484{
485 Q_D(QQmlSettingsLabs);
486 d->init();
487 qmlWarning(this) << "The Settings type from Qt.labs.settings is deprecated"
488 " and will be removed in a future release. Please use "
489 "the one from QtCore instead.";
490}
491
492void QQmlSettingsLabs::timerEvent(QTimerEvent *event)
493{
494 Q_D(QQmlSettingsLabs);
495 if (event->timerId() == d->timerId) {
496 killTimer(d->timerId);
497 d->timerId = 0;
498
499 d->store();
500 }
501 QObject::timerEvent(event);
502}
503
504QT_END_NAMESPACE
505
506#include "moc_qqmlsettings_p.cpp"
\inmodule QtCore
Definition qhash.h:837
QSettings * instance() const
QPointer< QSettings > settings
QQmlSettingsLabs * q_ptr
QVariant readProperty(const QMetaProperty &property) const
QHash< const char *, QVariant > changedProperties
static constexpr auto settingsWriteDelay
#define qCDebug(category,...)
#define Q_STATIC_LOGGING_CATEGORY(name,...)