概述

QSettings类提供了一种持久的、与平台无关的应用程序设置存储功能。
用户通常期望一个应用能在不同会话中记住其设置(窗口大小和位置,选项等)。在Windows上,这些信息通常存储在系统注册表中;在macOSiOS上,存储在属性列表文件中;在Unix系统上,由于缺乏标准,许多应用程序(包括KDE应用程序)使用INI文本文件。
QSettings是对这些技术的抽象,在可移植性的基础上,使您能够保存和恢复应用程序设置。它还支持自定义的存储格式。
QSettings的API基于QVariant,允许您以最小的努力保存大多数基于值的类型,如QString、QRect和QImage等。
如果您只需要一个非持久的基于内存的结构,请考虑使用QMap<QString, QVariant>替代。

基本用法

创建QSettings对象时,必须传递您的公司组织的名称以及应用程序的名称。例如,如果您的产品名称为Star Runner,公司名为MySoft,您应该如下构造QSettings对象:

QSettings settings("MySoft", "Star Runner");

可以在堆上(使用new)或栈上创建QSettings对象。构造和销毁QSettings对象非常快速。
如果在应用程序的多个地方使用QSettings,可以使用QCoreApplication::setOrganizationName()QCoreApplication::setApplicationName()来指定组织名称和应用程序名称,然后使用默认的QSettings构造函数:

QCoreApplication::setOrganizationName("MySoft");
QCoreApplication::setOrganizationDomain("mysoft.com");
QCoreApplication::setApplicationName("Star Runner");
...
QSettings settings;

(这里还指定了组织的互联网域。在设置了互联网域的情况下,macOS和iOS会使用互联网域来标识应用程序,而不是组织名称,因为macOS和iOS应用程序通常使用互联网域来标识自己。如果未设置域,将从组织名称派生一个虚假的域。有关详细信息,请参阅下面的特定平台的注释。)
QSettings存储设置。每个设置由一个QString(键)和一个QVariant(关联该键的数据)组成。使用setValue()来写入设置。例如:

settings.setValue("editor/wrapMargin", 68);

如果存在具有相同键的设置,新值将覆盖现有值。出于效率考虑,更改可能不会立即保存到永久存储中。(您可以随时调用sync()来提交更改。)
使用value()方法可以获取设置的值:

int margin = settings.value("editor/wrapMargin").toInt();

如果没有指定名称的设置,QSettings将返回一个空的QVariant(可以转换为整数0)。您可以通过向value()传递第二个参数来指定另一个默认值:

int margin = settings.value("editor/wrapMargin", 80).toInt();

要测试给定键是否存在,请调用contains()方法。要删除与键关联的设置,请调用remove()方法。要获取所有键的列表,请调用allKeys()方法。要删除所有键,请调用clear()方法。

QVariant和GUI类型

由于QVariantQt Core模块的一部分,因此不能提供转换函数到Qt GUI的数据类型,如QColor、QImage和QPixmap。换句话说,QVariant中没有toColor()、toImage()或toPixmap()等函数。
相反,您可以使用QVariant::value()qVariantValue()模板函数。例如:

QSettings settings("MySoft", "Star Runner");
QColor color = settings.value("DataPump/bgcolor").value<QColor>();

反向转换(例如,从QColor到QVariant)对于QVariant支持的所有数据类型,包括与GUI相关的类型都是自动的:

QSettings settings("MySoft", "Star Runner");
QColor color = palette().background().color();
settings.setValue("DataPump/bgcolor", color);

使用qRegisterMetaType()qRegisterMetaTypeStreamOperators()注册的自定义类型也可以使用QSettings进行存储。

部分和键的语法

设置键可以包含任何Unicode字符。Windows注册表和INI文件使用不区分大小写的键,而macOSiOS上的CFPreferences API使用区分大小写的键。为了避免可移植性问题,请遵循以下简单规则:

  1. 始终使用相同的大小写引用相同的键。例如,如果在代码的某个地方将键称为"text fonts",请不要在其他地方将其称为"Text Fonts"
  2. 避免只有大小写不同的键名称。例如,如果有一个名为"MainWindow"的键,请不要使用"mainwindow"保存另一个键。
  3. 不要在部分或键名中使用斜线(‘/‘和’’)。反斜杠字符用于分隔子键(见下文)。在Windows上,‘‘会被QSettings转换为’/’,从而使它们相同。
    您可以使用’/'字符作为分隔符来形成具有层次结构的键,类似于Unix文件路径。例如:
settings.setValue("mainwindow/size", win->size());
settings.setValue("mainwindow/fullScreen", win->isFullScreen());
settings.setValue("outputpanel/visible", panel->isVisible());

如果您想要保存或还原具有相同前缀的多个设置,可以使用beginGroup()指定前缀,并在结束时调用endGroup()。下面是相同的示例,但这次使用了组机制:

settings.beginGroup("mainwindow");
settings.setValue("size", win->size());
settings.setValue("fullScreen", win->isFullScreen());
settings.endGroup();

settings.beginGroup("outputpanel");
settings.setValue("visible", panel->isVisible());
settings.endGroup();

如果使用beginGroup()设置了组,大多数函数的行为会相应改变。组可以递归设置。
除了组,QSettings还支持"数组"概念。详细信息请参阅beginReadArray()beginWriteArray()

后备机制

假设您已经创建了一个QSettings对象,组织名称为MySoft,应用程序名称为Star Runner。在查找值时,按照以下顺序搜索最多四个位置:

  1. Star Runner应用程序的特定用户位置
  2. 所有MySoft应用程序的特定用户位置
  3. Star Runner应用程序的系统范围位置
  4. 所有MySoft应用程序的系统范围位置
    (有关Qt支持的不同平台上这些位置的信息,请参阅下面的特定平台的注释。)
    如果在第一个位置找不到键,则继续在第二个位置搜索,依此类推。这使您能够存储系统范围或组织范围的设置,并可以在每个用户或每个应用程序的基础上进行覆盖。要关闭此机制,请调用setFallbacksEnabled(false)。
    虽然可以从四个位置读取所有键,但只有第一个文件(与当前正在处理的应用程序相关的特定用户位置)可以写入。要写入其他文件,请省略应用程序名称和/或指定QSettings::SystemScope(与默认值QSettings::UserScope相对)。
    让我们通过一个示例来看:
QSettings obj1("MySoft", "Star Runner");
QSettings obj2("MySoft");
QSettings obj3(QSettings::SystemScope "MySoft", "Star Runner");
QSettings obj4(QSettings::SystemScope, "MySoft");

下表总结了哪些QSettings对象访问哪些位置。"X"表示该位置是与QSettings对象关联的主位置,用于读取和写入;"o"表示在读取时该位置用作后备。

【Qt之QSetting】介绍及使用_qt


这种机制的美妙之处在于它适用于Qt支持的所有平台,并且仍然提供了很大的灵活性,而无需指定任何文件名或注册表路径。

如果您想在所有平台上都使用INI文件而不是本地API,可以将QSettings::IniFormat作为QSettings构造函数的第一个参数,后跟作用域、组织名称和应用程序名称:

QSettings settings(QSettings::IniFormat, QSettings::UserScope,
                   "MySoft", "Star Runner");

Settings Editor示例允许您尝试不同设置位置以及打开或关闭后备机制。

恢复GUI应用程序的状态

QSettings经常用于存储GUI应用程序的状态。下面的示例演示了如何使用QSettings来保存和恢复应用程序主窗口的几何形状。

void MainWindow::writeSettings()
{
    QSettings settings("Moose Soft", "Clipper");

    settings.beginGroup("MainWindow");
    settings.setValue("size", size());
    settings.setValue("pos", pos());
    settings.endGroup();
}

void MainWindow::readSettings()
{
    QSettings settings("Moose Soft", "Clipper");

    settings.beginGroup("MainWindow");
    resize(settings.value("size", QSize(400, 400)).toSize());
    move(settings.value("", QPoint(200, 200)).toPoint());
    settings.endGroup();
}

有关为什么调用QWidget::resize()QWidget::move()而不是QWidget::setGeometry()以恢复窗口的几何形状的讨论,请参阅窗口几何形状。
readSettings()writeSettings()函数必须从主窗口的构造函数和close事件处理程序中调用,如下所示:

MainWindow::MainWindow()
{
    ...
    readSettings();
}

void MainWindow::closeEvent(QCloseEvent *event)
{
    if (userReallyWantsToQuit()) {
        writeSettings();
        event->accept();
    } else {
        event->ignore();
    }
}

有关使用QSettings的自包含示例,请参见Application示例。

同时从多个线程或进程访问设置

QSettings是可重入的。这意味着可以同时在不同的线程中使用不同的QSettings对象。即使这些QSettings对象引用相同的磁盘文件(或系统注册表中的相同条目),此保证仍然有效。如果通过一个QSettings对象修改了一个设置,这个更改将立即对在同一位置操作并且在同一进程中存在的任何其他QSettings对象可见。
可以安全地从不同的进程(可以是同时运行的应用程序的不同实例或完全不同的应用程序)读取和写入相同的系统位置。它使用建议性文件锁定和智能合并算法来确保数据的完整性。请注意,sync()方法会导入其他进程所做的更改(除了写入这个QSettings的更改)。

常用方法

以下是QSettings类的一些常用方法的介绍。

构造函数:

  • QSettings(const QString& organization, const QString& application = QString(), QObject *parent = nullptr)
  • 使用组织名称和应用程序名称创建一个QSettings对象。默认情况下,QSettings使用基于平台的本地存储机制。
  • organization:组织名称
  • application:应用程序名称(可选)
  • parent:父级QObject对象(可选)

读取和写入值:

  • value(const QString& key, const QVariant& defaultValue = QVariant()):读取指定键的值,如果键不存在则返回defaultValue。返回QVariant类型的值。
  • setValue(const QString& key, const QVariant& value):设置指定键的值。
  • remove(const QString& key):删除指定键及其对应的值。
  • clear():清除所有的设置键和值。

设置默认值:

  • setDefaultFormat(QSettings::Format format):为所有新创建的QSettings对象设置默认格式(例如,INI格式或注册表格式)。
  • setDefaultScope(QSettings::Scope scope):为所有新创建的QSettings对象设置默认作用域(用户范围或系统范围)。

读取和写入组:

  • beginGroup(const QString& prefix):开始一个以prefix为前缀的设置组。
  • endGroup():结束当前的设置组。

同步操作:

  • sync():将所有的更改写入到永久存储,确保数据的同步。通常,QSettings会自动定期执行这个操作,但在某些情况下可能需要手动调用。

获取设置信息:

  • fileName():返回与当前QSettings对象关联的文件名。
  • scope():返回当前QSettings对象的作用域(用户范围或系统范围)。
  • organizationName():返回当前QSettings对象的组织名称。
  • applicationName():返回当前QSettings对象的应用程序名称。

注意:QSettings还提供了其他一些方法,例如支持跨线程和进程读写设置,以及对设置键和值进行迭代和查询。

示例

以下是一个使用QSettings的示,包含了常用的成员方法:

#include <QCoreApplication>
#include <QSettings>
#include <QDebug>

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);

    // 创建 QSettings 对象
    QSettings settings("MyCompany", "MyApp");

    // 设置值
    settings.setValue("username", "John");
    settings.setValue("password", "123456");

    // 获取值
    QString username = settings.value("username").toString();
    QString password = settings.value("password").toString();

    qDebug() << "Username:" << username;
    qDebug() << "Password:" << password;

    // 检查是否存在某个键
    if (settings.contains("username")) {
        qDebug() << "The 'username' key exists";
    }

    // 移除一个键
    settings.remove("password");

    // 通过分组设置值,创建一个新的分组
    settings.beginGroup("Server");
    settings.setValue("host", "localhost");
    settings.setValue("port", 8080);
    settings.endGroup();

    // 在分组中获取值
    QString serverHost = settings.value("Server/host").toString();
    int serverPort = settings.value("Server/port").toInt();

    qDebug() << "Server Host:" << serverHost;
    qDebug() << "Server Port:" << serverPort;

    // 将更改刷新到磁盘上的配置文件中
    settings.sync();

    return a.exec();
}

结果

【Qt之QSetting】介绍及使用_应用程序_02

示例步骤:

  1. 创建了一个名为"MyApp"的应用程序设置,并设置了一些值。
  2. 通过value()方法获取这些值并输出。使用contains()方法检查特定的键是否存在,并使用remove()方法移除了"password"键。
  3. 使用beginGroup()和endGroup()方法创建了一个名为"Server"的分组,并在该分组中设置了一些值。
  4. 通过sync()方法将更改刷新到配置文件中。