QtMvvm  1.1.0
A mvvm oriented library for Qt, to create Projects for Widgets and Quick in parallel
The qsettingsgenerator Tool

Table of Contents

A tool to generate static, typesafe settings access without having to type key strings

The general idea is that instead of this:

QSettins settings;
int value = settings.value("my/key", 42).toInt();

You create a settings file like this:

<Settings name="Settings">
<Node key="my">
<Entry key="key" type="int" default="42"/>
</Node>
</Settings>

And can now access the data like this:

int value = Settings::instance()->my.key;

So in summary: First, using QSettings requires you to type in strings as keys - which cannot be checked by the compiler. With this tool you get a c++ member hirachie instead - which is checked by the compiler and thus typo free. Also, the values in the settings are made statically typed by using this generator. The second point was to support multiple backends, not only QSettings, without having to change the frontend code.

Features

Usage

Using QtMvvmCore adds a custom qmake compiler to you project. All you need to do is to create the settings xml definition and then add it to the pro file as SETTINGS_DEFINITIONS += mysettings.xml. This will create a header named "mysettings.h" you can include. That header contains the C++ generated settings class to access the settings.

Example

Create a pro file with and add the lines:

QT += mvvmcore
SETTINGS_DEFINITIONS += mysettings.xml

Create a file named mysettings.xml and fill it:

<?xml version="1.0" encoding="UTF-8" ?>
<Settings name="MySettings">
<Node key="my">
<Entry key="key" type="int" default="42"/>
</Node>
</Settings>

Finally, adjust you main to look like this:

#include <QCoreApplication>
#include <QDebug>
#include "mysettings.h"
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
int myKey = MySettings::instance()->my.key;
qDebug() << myKey;
return 0;
}

QML-Bindings

The tool can also generate QML-Bindings, so you can access the settings in the same way from QML. To do so, add to your pro file (in addition to SETTINGS_DEFINITIONS):

QML_SETTINGS_DEFINITIONS += mysettings.xml

And also add some QML metadata using the Qml element in the settings

<?xml version="1.0" encoding="UTF-8" ?>
<Settings name="MySettings">
<Qml uri="com.example.settings"
major="1"
minor="0"/>
<!-- ... -->
</Settings>

And with that, you can use the specified uri to access the settings from QML:

import com.example.settings 1.0
SomeItem {
someProperty: MySettings.my.key
}

XML-Format Documentation

The following sections document the different elements and attributes in the XML format used by the generator.

Elements

The possible elements of such a file.

Settings

The root Element of the XML document. It defines meta-stuff and the actual nodes and entries.

Attributes

Name Type Default/Required Description
name string filename The C++ class name of the generated class
prefix string empty Specify an export macro. The value is put before the class definition, if you want to export the generated class from a dynamic library
baseKey string empty A base settings key that is prepended to all keys
scope InstanceScope DestroyOnAppDestroy The destruction scope of the create service instance

Child elements

Name XML-Type Limits Description
Include Include 0 - ∞ A header file to be included
Backend Backend 0 - 1 The definition of the settings backend to use
Qml Qml 0 - 1 Special configuration parameters for the QML bindings
TypeMapping TypeMapping 0 - ∞ Type mappings to map abstract type names to C++ types
<node> NodeContent Elements 0 - ∞ General "node-like" elements, that can be inside any node-like element

Include

Describes a file to be included via a c++ include directive. You can use them if you want to make use of special classes in your generated classes. Both global includes (#include <header>) and local includes (#include "header") are supported.

Attributes

Name Type Default/Required Description
local bool false Specifies whether the include is a global or a local include

Content

The content of this element must be a string. More specific the header to be included. It becomes the content of the include directive. Specifiy it without the braces or quores, as thoes are added automatically.

Backend

The backend element can be used to select a different QtMvvm::ISettingsAccessor instead of the standard QtMvvm::QSettingsAccessor.

Attributes

Name Type Default/Required Description
class string required The C++ class name of the backend to be used. Must be a class that implements QtMvvm::ISettingsAccessor

Child elements

Name XML-Type Limits Description
Param Param 0 - ∞ A parameter to be passed to the backends constructor, before the QObject* parent

Param

A generic parameter to be passed to C++ method.

Attributes

Name Type Default/Required Description
type string required The C++ class name of type of the parameter.
asStr bool false Specify how the element content should be interpreted

Content

The content of a param represents the value of the parameter. How the content is interpreted depends on the asStr attribute.

If it is set to false (the default), the content must be c++ code and is copied to the generated class as is. The code must be an expression that evalutes to a single value that is implicitly convertible to the given type. If type was for example int, valid expressions could be:

<Param type="int">-1</Param>
<Param type="int">10 + 20</Param>
<Param type="int">qRand()</Param>

If set to true, the content is assumed to be a string. You don't need to specify quotation marks around the string. That string is used to initialize a QVariant that is then converted to type - in other words, the type you use must be variant convertible from a string. This can be used to, for example, create a QDate from a string with the value of <Param type="QDate" asStr="true">2018-05-09</Param>.

Qml

The Qml element contains special properties that are only used for QML binding generation.

Attributes

Name Type Default/Required Description
uri string required The QML-Import URI for the QML module
major int 1 The major version of the generated module
minor int 0 The minor version of the generated module
type QmlRegistrationMode Singleton The mode on how to register the instance in QML
register bool true Specify, if the type should be registered automatically
header string empty A custom path to the generate C++-Code of the settings generator, if it can't be the default path

TypeMapping

Type mappings allow you to specify the C++-type of an arbitrary type to be replaced by in the generated code. This can be useful when importing a Settings-XML file.

Attributes

Name Type Default/Required Description
key string required The fake type name
type string required The C++-type to replace it with

NodeContent Elements

Elements that have the NodeContent as child type can have a combination of the following actual child elements. Can can be unordered and mixed in any way:

Node

The node represents a sub-group within the settings. All elements within the node will have the node's key prepended to their key (seperated by a /)

Attributes

Name Type Default/Required Description
key string required The name (and group key) of the node

Child elements

Name XML-Type Limits Description
<node> NodeContent Elements 0 - ∞ General "node-like" elements, that can be inside any node-like element

Entry

The Entry element is an extension of the Node type. This means it's the same as a Node, but with additional properties.

The entry represents a leaf entry in the settings, with a value that can be loaded and stored from the settings via the entry's key (within the node subgroups). Entries themselves can also be used as a node as well, and thus contain child elements, too.

The default value can be specified in 2 ways. Either as a string (via the attribute) that is converted to the target type using QVariant, or via the <Code> child element, that contains actual C++-Code that evaluates to a default value.

See also
QtMvvm::SettingsEntry

Attributes

Name Type Default/Required Description
type string required The C++-type that this entry saves and loads. Can be a virtual type, that is resolved by a TypeMapping
qmlGroupKey string <key>Group The name the property that holds the entries sub-nodes for the QML binding
default string empty A default value (as string) to be returned if the entry is not stored in the settings
tr bool false Specify whether the default value should be translated
trContext string filename A custom translation context to use instead of the filename

Additional Child elements

Name XML-Type Limits Description
Code settings_generator_elements_Code 0 - 1 C++-Code to create a default value

ListNode

The ListNode element is an extension of the Node type. This means it's the same as a Node, but with additional properties.

The ListNode is a special node that allows you to easily store lists of elements. All children that are defined within a listnode are part of the "array elements", so you will access elements in a list node via listNode[2].group.entry and can append and remove elements from that list.

See also
QtMvvm::SettingsListNode

ImportType

The Imports allow you to include other files within this one as sub elements. You can include either a Settings-Generator-XML (this file) or a Settings-XML. The imported file is then treated like it's contents where simply defined instead of the import element, allowing seemless combination of different files.

You can use the rootNode to instead of importing the root element in the importet file, go down to the node that matches the key defined by rootNode and only import that part.

Attributes

Name Type Default/Required Description
required bool true Specify, if the import is required or optional
rootNode string empty A root node withing the imported file to use as a starting point

Content

The content of the element is a string - the path to the file to be imported. If the path is a relative path, it is resolved relative to the file that is importing it, aka "this" file.

XML-Types

The XML-Types are not elements, but values of attributes etc. that have been defined for the file.

InstanceScope

InstanceScope is a simple enum with the following allowed values:

Name Description
DestroyOnAppQuit Sets the scope of the created mvvm service to QtMvvm::ServiceRegistry::DestroyOnAppQuit
DestroyOnAppDestroy Sets the scope of the created mvvm service to QtMvvm::ServiceRegistry::DestroyOnAppDestroy
DestroyOnRegistryDestroy Sets the scope of the created mvvm service to QtMvvm::ServiceRegistry::DestroyOnRegistryDestroy
DestroyNever Sets the scope of the created mvvm service to QtMvvm::ServiceRegistry::DestroyNever

QmlRegistrationMode

InstanceScope is a simple enum with the following allowed values:

Name Description
Singleton Register the QML type as a singleton instance
Uncreatable Register the QML type as a uncreatable type
Creatable Register the QML type as a normal, constructable type

Sample settings generator XML file

The following code block is a sample of a settings generator XML file. It's a little bigger to show of the capabilities

<?xml version="1.0" encoding="UTF-8" ?>
<Settings name="TestSettings"
prefix="SOME_EXPORT"
baseKey="tests"
scope="DestroyOnAppQuit">
<Include>QtCore/QDateTime</Include>
<Include local="false">QtCore/QUrl</Include>
<Include local="true">testbackend.h</Include>
<Backend class="TestBackend">
<Param type="QString" asStr="true">Test Backend</Param>
<Param type="int">42</Param>
</Backend>
<TypeMapping key="range" type="int"/>
<Node key="emptyNode"/>
<Entry key="emptyEntry"
type="bool"/>
<Entry key="advancedEntry"
type="QString"
qmlGroupKey="qmlAdvancedEntry"
default="Hello World"
tr="true"
trContext="some_context"/>
<Entry key="codeEntry"
type="QUrl">
<Code>QUrl::fromLocalFile(QStringLiteral("/path/to/something"))</Code>
</Entry>
<Node key="parentNode">
<Node key="emptyChildNode"/>
<Node key="fullChildNode">
<Entry key="replaceEntry"
type="range"
default="42"/>
</Node>
<Entry key="parentEntry"
type="bool"
default="true">
<Node key="subNode"/>
<Entry key="nodeWithCodeEntry"
type="int">
<Node key="someNode"/>
<Code>
qRound(42.8)
</Code>
</Entry>
<Entry key="leafEntry"
type="QString"
default="translate me"
tr="true"/>
</Entry>
</Node>
<Entry key="voidEntry"
type="void"/>
<Entry key="variantEntry"
type="QVariant"/>
<Entry key="simpleListEntry"
type="QList&lt;int&gt;">
<Code>{42}</Code>
</Entry>
<ListNode key="listNode">
<Entry key="simpleChild"
type="bool"/>
<Node key="someNode">
<Entry key="deepChild"
type="int"
default="22"/>
<Entry key="deepParent"
type="QString"
default="___">
<Entry key="simpleChild"
type="bool"
default="true"/>
</Entry>
</Node>
<ListNode key="childList">
<Entry key="valueEntry"
type="double"
default="4.2"/>
</ListNode>
</ListNode>
</Settings>

The XSD for the settings files

The following file is the XSD the parser operates on. You can use it to verify your settings xml files.

<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:qxg="https://skycoder42.de/xml/schemas/QXmlCodeGen">
<!-- QXG Definitions -->
<qxg:config class="SettingsGeneratorBase"
stdcompat="true"
schemaUrl="qrc:/schemas/qsettingsgenerator.xsd">
<qxg:include>QtCore/QHash</qxg:include>
</qxg:config>
<qxg:method name="read_type_mapping" type="QHash&lt;QString, QString&gt;" asGroup="true"/>
<qxg:method name="read_included_file" type="NodeContentGroup"/>
<!-- Basic Types -->
<xs:simpleType name="QmlRegistrationMode">
<xs:restriction base="xs:string">
<xs:enumeration value="Singleton"/>
<xs:enumeration value="Uncreatable"/>
<xs:enumeration value="Creatable"/>
</xs:restriction>
</xs:simpleType>
<xs:simpleType name="InstanceScope">
<xs:restriction base="xs:string">
<xs:enumeration value="DestroyOnAppQuit"/>
<xs:enumeration value="DestroyOnAppDestroy"/>
<xs:enumeration value="DestroyOnRegistryDestroy"/>
<xs:enumeration value="DestroyNever"/>
</xs:restriction>
</xs:simpleType>
<!-- Type definitions -->
<xs:complexType name="IncludeType">
<xs:simpleContent>
<xs:extension base="xs:string" qxg:member="includePath">
<xs:attribute name="local" type="xs:boolean" use="optional" default="false"/>
</xs:extension>
</xs:simpleContent>
</xs:complexType>
<xs:complexType name="QmlType">
<xs:attribute name="uri" type="xs:string" use="required"/>
<xs:attribute name="major" type="xs:integer" use="optional" default="1"/>
<xs:attribute name="minor" type="xs:integer" use="optional" default="0"/>
<xs:attribute name="type" type="QmlRegistrationMode" use="optional" default="Singleton"/>
<xs:attribute name="register" type="xs:boolean" use="optional" default="true" qxg:member="autoRegister"/>
<xs:attribute name="header" type="xs:string" use="optional"/>
</xs:complexType>
<xs:complexType name="ImportType">
<xs:simpleContent>
<xs:extension base="xs:string" qxg:member="importPath">
<xs:attribute name="required" type="xs:boolean" use="optional" default="true"/>
<xs:attribute name="rootNode" type="xs:string" use="optional"/>
</xs:extension>
</xs:simpleContent>
</xs:complexType>
<xs:complexType name="TypeMappingEntry">
<xs:attribute name="key" type="xs:string" use="required"/>
<xs:attribute name="type" type="xs:string" use="required"/>
</xs:complexType>
<xs:group name="TypeMappingGroup">
<xs:sequence>
<xs:element maxOccurs="unbounded" minOccurs="0" name="TypeMapping" type="TypeMappingEntry"/>
</xs:sequence>
</xs:group>
<xs:complexType name="ParamType">
<xs:simpleContent>
<xs:extension base="xs:string" qxg:member="value">
<xs:attribute name="type" type="xs:string" use="required"/>
<xs:attribute name="asStr" type="xs:boolean" use="optional" default="false"/>
</xs:extension>
</xs:simpleContent>
</xs:complexType>
<xs:complexType name="BackendType">
<xs:sequence>
<xs:element maxOccurs="unbounded" minOccurs="0" name="Param" type="ParamType"/>
</xs:sequence>
<xs:attribute name="class" type="xs:string" use="required" qxg:member="className"/>
</xs:complexType>
<xs:group name="NodeContentGroup">
<xs:sequence>
<xs:choice maxOccurs="unbounded" minOccurs="0" qxg:member="contentNodes">
<xs:element name="Node" type="NodeType"/>
<xs:element name="Entry" type="EntryType"/>
<xs:element name="ListNode" type="ListNodeType"/>
<xs:element name="Import" type="ImportType" qxg:method="read_included_file"/>
</xs:choice>
</xs:sequence>
</xs:group>
<xs:complexType name="NodeType" qxg:declare="true">
<xs:group ref="NodeContentGroup" qxg:inherit="true"/>
<xs:attribute name="key" type="xs:string" use="required"/>
</xs:complexType>
<xs:complexType name="ListNodeType" qxg:declare="true">
<xs:complexContent>
<xs:extension base="NodeType"/>
</xs:complexContent>
</xs:complexType>
<xs:complexType name="EntryType" qxg:declare="true">
<xs:complexContent>
<xs:extension base="NodeType">
<xs:sequence>
<xs:element maxOccurs="1" minOccurs="0" name="Code" type="xs:string"/>
</xs:sequence>
<xs:attribute name="type" type="xs:string" use="required"/>
<xs:attribute name="qmlGroupKey" type="xs:string" use="optional"/>
<xs:attribute name="default" type="xs:string" use="optional" qxg:member="defaultValue"/>
<xs:attribute name="tr" type="xs:boolean" default="false" use="optional"/>
<xs:attribute name="trContext" type="xs:string" use="optional"/>
</xs:extension>
</xs:complexContent>
</xs:complexType>
<xs:complexType name="SettingsType">
<xs:sequence>
<xs:element maxOccurs="unbounded" minOccurs="0" name="Include" type="IncludeType" qxg:member="includes"/>
<xs:element maxOccurs="1" minOccurs="0" name="Backend" type="BackendType"/>
<xs:element maxOccurs="1" minOccurs="0" name="Qml" type="QmlType"/>
<xs:group ref="TypeMappingGroup" qxg:member="typeMappings" qxg:method="read_type_mapping"/>
<xs:group ref="NodeContentGroup" qxg:inherit="true"/>
</xs:sequence>
<xs:attribute name="name" type="xs:string" use="optional"/>
<xs:attribute name="prefix" type="xs:string" use="optional"/>
<xs:attribute name="baseKey" type="xs:string" use="optional"/>
<xs:attribute name="scope" type="InstanceScope" use="optional"/>
</xs:complexType>
<!-- root elements-->
<xs:element name="Settings" type="SettingsType"/>
</xs:schema>