QSettings::WriteFunc

2025-05-27

QSettings::WriteFunc とは?

QSettings::WriteFunc は、以下のようなシグネチャを持つ関数へのポインタとして定義されています。

typedef bool (*WriteFunc)(QIODevice &device, const QSettings::SettingsMap &map);

この関数は、QSettings::registerFormat() メソッドに ReadFunc と共に渡されます。

  • bool 戻り値: 書き込みが成功した場合は true を、失敗した場合は false を返します。
  • const QSettings::SettingsMap &map: QSettings オブジェクトが現在持っているすべてのキーと値のペアを含むマップです。SettingsMapQMap<QString, QVariant> のエイリアスです。このマップの内容を device に書き出すのが WriteFunc の役割です。
  • QIODevice &device: 設定を書き込むためのデバイス(ファイル、メモリなど)。このデバイスに対して、カスタムフォーマットのデータを書き込みます。

WriteFunc の役割

WriteFunc は、QSettings がメモリ上の設定データを永続ストレージ(ファイルなど)に保存する際に呼び出されます。この関数内で、開発者は map に含まれるキーと値のデータをどのように device に書き込むかを自由に実装できます。

たとえば、設定データをXML形式やJSON形式でファイルに保存したい場合、WriteFunc の中で QXmlStreamWriterQJsonDocument などを使用して、map の内容をXMLやJSONデータとして device に書き込むことができます。

registerFormat() との関連

QSettings::WriteFunc は、主に QSettings::registerFormat() 静的メソッドと組み合わせて使用されます。

static QSettings::Format registerFormat(const QString &extension,
                                        QSettings::ReadFunc readFunc,
                                        QSettings::WriteFunc writeFunc,
                                        Qt::CaseSensitivity caseSensitivity = Qt::CaseSensitive);

このメソッドに WriteFuncReadFunc(読み込み用の関数ポインタ)を渡すことで、指定された extension を持つファイルに対して、独自の読み書き処理を QSettings に登録することができます。登録が成功すると、QSettings::Format 型の新しいカスタムフォーマットが返され、このフォーマットを使って QSettings オブジェクトを作成できるようになります。

#include <QSettings>
#include <QIODevice>
#include <QVariant>
#include <QDebug> // デバッグ出力用

// カスタム設定書き込み関数
bool myCustomWriteFunc(QIODevice &device, const QSettings::SettingsMap &map)
{
    // ここで、map の内容を device に独自の形式で書き込む
    // 例: シンプルなテキスト形式で書き出す
    QTextStream out(&device);
    for (auto it = map.constBegin(); it != map.constEnd(); ++it) {
        out << it.key() << "=" << it.value().toString() << "\n";
    }
    return true; // 成功
}

// (ReadFunc も同様に実装する必要がある)
bool myCustomReadFunc(QIODevice &device, QSettings::SettingsMap &map)
{
    // 読み込みのロジック
    return true;
}


int main(int argc, char *argv[])
{
    // ... QCoreApplication の初期化など ...

    // カスタムフォーマットを登録
    QSettings::Format customFormat = QSettings::registerFormat(
        "myformat",        // 拡張子
        &myCustomReadFunc, // 読み込み関数
        &myCustomWriteFunc // 書き込み関数
    );

    if (customFormat == QSettings::InvalidFormat) {
        qDebug() << "Failed to register custom format!";
        return 1;
    }

    // カスタムフォーマットを使用して QSettings オブジェクトを作成
    QSettings settings("path/to/myconfig.myformat", customFormat);

    // 設定を書き込む
    settings.setValue("user/name", "Taro Yamada");
    settings.setValue("app/version", 1.0);
    settings.sync(); // 変更を強制的に書き込む

    // ... アプリケーションの実行 ...

    return 0;
}


QSettings::WriteFunc における一般的なエラーとトラブルシューティング

書き込み関数が呼び出されない(設定が保存されない)

これは最もよくある問題の一つです。

  • 原因3: QSettings オブジェクトのライフサイクル QSettings オブジェクトがスコープを抜けて破棄される前に、設定が書き込まれる保証はありません(sync() を呼ばない限り)。一時的な QSettings オブジェクトを使用している場合、設定がファイルに書き込まれる前にオブジェクトが破棄されてしまうことがあります。

    • トラブルシューティング
      設定を書き込みたい範囲で QSettings オブジェクトが有効であることを確認してください。特に、GUIアプリケーションの場合は、MainWindowcloseEvent() などで sync() を呼ぶのが一般的です。
  • 原因2: QSettings::registerFormat() の呼び出しが遅い、または正しくない QSettings オブジェクトが作成される前に、カスタムフォーマットが QSettings::registerFormat() で登録されている必要があります。また、登録時に QSettings::WriteFunc の関数ポインタが正しく渡されているか確認してください。

    • トラブルシューティング
      QCoreApplication::setOrganizationName()QCoreApplication::setApplicationName() と同様に、アプリケーションの起動時(main 関数内など)に QSettings::registerFormat() を一度だけ呼び出すようにしてください。
  • 原因1: QSettings::sync() の呼び忘れ QSettings は、効率化のために設定変更をすぐにファイルに書き込まない場合があります。明示的に sync() を呼び出すことで、メモリ上の変更を永続ストレージにフラッシュさせることができます。

    • トラブルシューティング
      QSettings::setValue() などで値を設定した後、必ず settings.sync(); を呼び出してください。アプリケーション終了時に QSettings オブジェクトが破棄される際にも自動的に sync() が呼ばれますが、確実に書き込みたい場合は明示的な呼び出しが推奨されます。

書き込みはされるが、内容が破損している、または読めない

  • 原因3: データエンコーディングの問題 テキストベースのカスタムフォーマットの場合、文字エンコーディングが原因でデータが破損することがあります。

    • トラブルシューティング
      QTextStream を使う場合は、QTextStream::setCodec() で適切なエンコーディング(例: QStringConverter::Utf8)を設定してください。バイナリデータの場合は、QDataStream を使用し、バージョン管理を検討してください。
  • 原因2: QIODevice の使い方誤り QIODevice は読み書きを行うためのインターフェースです。正しくオープンされ、エラーなく書き込みが行われているか確認する必要があります。

    • トラブルシューティング
      device.write() の戻り値を確認して、書き込みが成功しているかチェックしてください。また、device.error()device.errorString() を使って、デバイスのI/Oエラーを検出することもできます。ファイルパスのパーミッション問題なども考えられます。
  • 原因1: WriteFuncReadFunc の不整合 カスタムフォーマットでは、WriteFunc で書き込んだデータ形式と、ReadFunc で読み込むデータ形式が完全に一致している必要があります。どちらか一方の実装が誤っていると、データが正しく保存・読み込みできません。

    • トラブルシューティング
      • WriteFunc でデータを書き込む際に、すべてのキーと値が正しくシリアライズされているか確認してください。特に QVariant の型変換に注意が必要です。
      • ReadFunc でデータを読み込む際に、WriteFunc で書き込んだ順序や形式と一致するようにデシリアライズしているか確認してください。
      • デバッグ出力(qDebug())を使って、WriteFunc で書き出すデータと、ReadFunc で読み込んだデータが一致しているか詳細に検証してください。

複雑なデータ型(カスタムクラスなど)の保存・読み込みができない

  • 原因1: QVariant への登録忘れ QSettings は基本的に QVariant で表現できるデータ型を扱います。カスタムクラスを QVariant で扱えるようにするためには、Q_DECLARE_METATYPE マクロと qRegisterMetaType() を使用して型を登録する必要があります。さらに、QDataStream を使ってカスタムクラスをシリアライズ/デシリアライズできるように、operator<<operator>> をオーバーロードする必要があります。
    • トラブルシューティング
      カスタムクラスが QVariant に格納できるか、そして QDataStream で正しく読み書きできるかを確認してください。QSettings::WriteFunc では QSettings::SettingsMap (QMap<QString, QVariant>) が渡されるため、QVariant がカスタムクラスを保持できることが前提となります。

ファイルパスやパーミッションの問題

  • 原因2: 書き込み権限がない アプリケーションが設定ファイルを書き込むディレクトリに対して、適切な書き込み権限がない場合があります。

    • トラブルシューティング
      特にシステムディレクトリや保護されたディレクトリに書き込もうとしている場合、権限の問題が発生しやすいです。ユーザーのホームディレクトリなど、書き込みが許可されている場所にファイルを保存するようにしてください。Linux/macOSではパーミッション、WindowsではUAC(ユーザーアカウント制御)が関係することもあります。
  • 原因1: ファイルパスの誤り QSettings オブジェクトのコンストラクタで指定したファイルパスが正しくない場合、期待する場所に設定ファイルが作成されません。

    • トラブルシューティング
      QSettings コンストラクタで指定したファイルパス(カスタムフォーマットの場合は拡張子も含む)が、アプリケーションが書き込みを許可されている場所にあるか確認してください。デバッグ出力でファイルパスをログに出力し、実際にそのパスにファイルが存在するか手動で確認することも有効です。

QSettings::registerFormat() のフォーマット競合

  • 原因1: 同じ拡張子で異なるフォーマットを登録しようとしている QSettings::registerFormat() は一度登録されると、その拡張子に対して別の ReadFuncWriteFunc を登録し直すことはできません。
    • トラブルシューティング
      アプリケーション内でカスタムフォーマットの登録は一度だけ行われるようにしてください。もし異なるフォーマットを扱いたい場合は、異なる拡張子を使用するか、既存のフォーマットの登録を解除する(ただし、これはQtで直接サポートされていないため、通常は避けるべきです)など、設計を見直す必要があります。

QSettings::SettingsMap の操作ミス

  • 原因1: QSettings::SettingsMap を適切に扱っていない WriteFunc に渡される SettingsMapconst 参照なので、変更することはできません。このマップからすべての設定を抽出し、カスタムフォーマットで書き出す必要があります。
    • トラブルシューティング
      SettingsMap は読み取り専用であることを理解し、その内容を全てファイルに書き出すロジックを確実に実装してください。キーや値の取り出し間違い、型変換のミスなどがデータの欠落や破損につながることがあります。
  • エラーハンドリング
    WriteFuncReadFunc の中でファイルI/Oエラーが発生した場合に、false を適切に返すように実装し、呼び出し元で QSettings::status() をチェックするようにしてください。これにより、書き込みや読み込みの失敗をアプリケーションで検知できます。
  • 単体テストを書く
    WriteFuncReadFunc のペアに対して、独立した単体テストを書くことで、入出力の整合性を自動的に検証できます。
  • シンプルなケースから始める
    最初から複雑なデータ構造を扱うのではなく、まず QStringint のような単純な値をいくつか保存・読み込みするカスタムフォーマットを実装し、それが正しく動作することを確認してから、徐々に複雑なデータ型や構造を追加していくのが良いでしょう。
  • qDebug() を多用する
    WriteFuncReadFunc の各ステップで、どのようなデータが処理されているか、どのようなエラーが発生しているか、qDebug() を使って詳細なログを出力することで、問題の特定が非常に容易になります。


QSettings::WriteFunc のプログラミング例

この例では、QSettings を使って、独自の「単純なテキスト形式」で設定を保存する方法を示します。この形式では、各設定は「キー=値」の形式で1行に書き込まれ、空行は無視されます。

ヘッダーファイルのインクルード

必要なQtのヘッダーファイルをインクルードします。

#include <QCoreApplication> // アプリケーションの基本機能
#include <QSettings>        // QSettingsクラス
#include <QIODevice>        // I/Oデバイスの基本クラス
#include <QTextStream>      // テキストの読み書き
#include <QVariant>         // 汎用データ型
#include <QDebug>           // デバッグ出力

カスタム WriteFunc の実装

QSettings::WriteFunc のシグネチャに合わせた関数を定義します。この関数は、QSettings::SettingsMap(キーと値のペアのマップ)の内容を QIODevice に書き込みます。

// カスタム設定書き込み関数
// QSettings::WriteFunc のシグネチャに合わせる
bool myCustomWriteFunc(QIODevice &device, const QSettings::SettingsMap &map)
{
    // QIODevice を QTextStream でラップしてテキストとして書き込めるようにする
    QTextStream out(&device);
    out.setCodec("UTF-8"); // UTF-8 エンコーディングを使用

    // 設定マップの各エントリを "キー=値" の形式で書き込む
    for (auto it = map.constBegin(); it != map.constEnd(); ++it) {
        // QVariant の値を QString に変換して書き込む
        out << it.key() << "=" << it.value().toString() << "\n";
    }

    // 書き込みが成功したことを示す
    return true;
}

解説

  • return true: 書き込み処理が成功した場合は true を返します。
  • it.key() << "=" << it.value().toString() << "\n": 各キーと値を = で結合し、改行を追加して1行として書き込みます。QVariant の値を toString()QString に変換しています。これにより、数値や真偽値などもテキストとして保存されます。
  • for (auto it = map.constBegin(); it != map.constEnd(); ++it): マップ内のすべてのキーと値のペアをイテレートします。
  • QTextStream out(&device): QIODevice をテキストストリームとして扱い、行単位での書き込みを容易にします。
  • const QSettings::SettingsMap &map: QSettings オブジェクトが保持しているすべての設定データ(キーと値のペア)を含むマップです。
  • QIODevice &device: これは設定を書き込むファイルやメモリバッファなどのデバイスを表します。

カスタム ReadFunc の実装 (読み込みも必要)

WriteFunc で書き込んだフォーマットを正しく読み込むためには、対応する QSettings::ReadFunc も必要です。

// カスタム設定読み込み関数
// QSettings::ReadFunc のシグネチャに合わせる
bool myCustomReadFunc(QIODevice &device, QSettings::SettingsMap &map)
{
    QTextStream in(&device);
    in.setCodec("UTF-8"); // 書き込み時と同じエンコーディングを使用

    // マップをクリアして、新しい設定を格納する
    map.clear();

    while (!in.atEnd()) {
        QString line = in.readLine().trimmed(); // 1行読み込み、前後の空白を除去
        if (line.isEmpty()) {
            continue; // 空行はスキップ
        }

        int equalsIndex = line.indexOf('=');
        if (equalsIndex > 0) {
            QString key = line.left(equalsIndex).trimmed();
            QString value = line.mid(equalsIndex + 1).trimmed();
            map[key] = value; // 読み込んだ値をマップに格納(QVariant は自動的に型推論される)
        }
    }

    return true; // 読み込みが成功したことを示す
}

QSettings::registerFormat() による登録と使用

main 関数内で、これらのカスタム関数を QSettings に登録し、使用します。

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

    // アプリケーション情報の設定 (QSettings で推奨)
    QCoreApplication::setOrganizationName("MyCompany");
    QCoreApplication::setApplicationName("MyApp");

    // 1. カスタムフォーマットの登録
    // "myconfig" という拡張子に対して、myCustomReadFunc と myCustomWriteFunc を登録
    QSettings::Format customFormat = QSettings::registerFormat(
        "myconfig",          // ファイルの拡張子
        &myCustomReadFunc,   // 読み込み関数へのポインタ
        &myCustomWriteFunc,  // 書き込み関数へのポインタ
        Qt::CaseSensitive    // キーの大文字小文字を区別する
    );

    if (customFormat == QSettings::InvalidFormat) {
        qCritical() << "カスタムフォーマットの登録に失敗しました!";
        return 1; // 登録失敗で終了
    }

    // 2. カスタムフォーマットを使用して QSettings オブジェクトを作成
    // "settings.myconfig" というファイルに設定が保存される
    QSettings settings("settings.myconfig", customFormat);

    // 3. 設定の書き込み
    qDebug() << "設定を書き込み中...";
    settings.setValue("user/name", "山田 太郎");
    settings.setValue("app/version", 1.23);
    settings.setValue("network/timeout_ms", 5000);
    settings.setValue("debug/enabled", true);

    // QSettings は自動的に変更をフラッシュしない場合があるので、明示的に sync() を呼び出す
    // これにより、myCustomWriteFunc が呼び出され、設定がファイルに書き込まれる
    settings.sync();
    if (settings.status() == QSettings::NoError) {
        qDebug() << "設定の書き込みが成功しました。";
    } else {
        qDebug() << "設定の書き込み中にエラーが発生しました:" << settings.status();
    }


    // 4. 設定の読み込み(テストのために別の QSettings オブジェクトで読み込むか、
    //    アプリケーションを再起動して確認するのが一般的)
    qDebug() << "\n設定を読み込み中...";
    QSettings readSettings("settings.myconfig", customFormat);

    qDebug() << "User Name:" << readSettings.value("user/name").toString();
    qDebug() << "App Version:" << readSettings.value("app/version").toDouble();
    qDebug() << "Network Timeout:" << readSettings.value("network/timeout_ms").toInt() << "ms";
    qDebug() << "Debug Enabled:" << readSettings.value("debug/enabled").toBool();

    return a.exec();
}
  1. 上記のコードを .cpp ファイルとして保存します(例: main.cpp)。
  2. QMake (.pro ファイル) または CMake (CMakeLists.txt ファイル) を使用してプロジェクトをビルドします。
    • QMake の場合 (.pro)
      QT       += core
      
      SOURCES += main.cpp
      
      # アプリケーションの名前と組織名を指定(QSettingsで使用される)
      QMAKE_TARGET = custom_settings_example
      ORGANIZATION_NAME = MyCompany
      APPLICATION_NAME = MyApp
      
    • CMake の場合 (CMakeLists.txt)
      cmake_minimum_required(VERSION 3.16)
      project(CustomSettingsExample LANGUAGES CXX)
      
      find_package(Qt6 COMPONENTS Core REQUIRED)
      
      add_executable(custom_settings_example main.cpp)
      target_link_libraries(custom_settings_example PRIVATE Qt6::Core)
      
      # QSettingsで組織名とアプリケーション名を設定
      set_property(TARGET custom_settings_example PROPERTY MACOSX_BUNDLE_GUI_IDENTIFIER "com.MyCompany.MyApp")
      set_property(TARGET custom_settings_example PROPERTY MACOSX_BUNDLE_ORGANIZATION_NAME "MyCompany")
      set_property(TARGET custom_settings_example PROPERTY MACOSX_BUNDLE_EXECUTABLE_NAME "MyApp")
      
  3. ビルドして実行します。

実行後、実行ファイルと同じディレクトリに settings.myconfig というファイルが作成されます。その内容を見ると、以下のようになっているはずです。

user/name=山田 太郎
app/version=1.23
network/timeout_ms=5000
debug/enabled=true


QSettings::WriteFunc を使用しない代替方法

標準の QSettings フォーマットを使用する

最もシンプルで推奨されるアプローチは、Qt が提供する標準の QSettings フォーマットを使用することです。これにより、クロスプラットフォームの互換性、パフォーマンス、保守性が保証されます。

  • QSettings::IniFormat: 常にINIファイル形式で設定を保存します。これは、設定ファイルをユーザーが手動で編集しやすいという利点があります。
  • QSettings::NativeFormat: 各プラットフォームのネイティブな設定保存メカニズムを使用します。
    • Windows: レジストリ
    • macOS: CFPreferences (Property List .plist ファイル)
    • Unix/Linux: INIファイル(デスクトップ環境やXDG Base Directory Specificationに従う)

いつ使うべきか

  • 設定ファイルをテキストエディタで編集可能にしたい場合(IniFormat)。
  • 設定ファイルをユーザーが直接編集する可能性が低い場合(NativeFormat)。
  • プラットフォーム固有の慣習に従いたい場合。
  • 特別なファイル形式の要件がない場合。


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

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);
    QCoreApplication::setOrganizationName("MyCompany");
    QCoreApplication::setApplicationName("MyApp");

    // ネイティブフォーマットを使用
    QSettings nativeSettings;
    nativeSettings.setValue("window/size", QSize(800, 600));
    qDebug() << "Native Format Window Size:" << nativeSettings.value("window/size").toSize();

    // INIフォーマットを使用
    QSettings iniSettings("config.ini", QSettings::IniFormat);
    iniSettings.setValue("user/theme", "dark");
    qDebug() << "INI Format User Theme:" << iniSettings.value("user/theme").toString();

    return a.exec();
}

独自のファイル形式を完全に手動で扱う

QSettings の抽象化を完全に避け、ファイルI/Oを直接行って独自のフォーマットでデータを保存する方法です。

  • シンプルなテキストファイル
    QTextStream を使用して、独自の区切り文字や書式でテキストを書き込みます。最も自由度が高いですが、パーシングロジックはすべて自分で実装する必要があります。
  • バイナリ形式
    QDataStream を使用して、Qtの型を直接バイナリ形式でファイルに書き込みます。パフォーマンスが良く、ファイルサイズが小さくなる傾向がありますが、人間が読み取ることができません。
  • JSON (JavaScript Object Notation)
    QJsonDocumentQJsonObjectQJsonArray を使用して、JSON形式でデータを保存・読み込みます。軽量で読みやすく、ウェブサービスとの連携にも適しています。
  • XML (Extensible Markup Language)
    QXmlStreamWriterQXmlStreamReader を使用して、構造化されたデータをXMLファイルに保存・読み込みます。

いつ使うべきか

  • QSettings のキー/値ペアの単純な構造では表現できないデータを持つ場合。
  • 既存のファイルフォーマットに合わせる必要がある場合。
  • バージョン管理やスキーマ検証など、より高度なデータ管理が必要な場合。
  • 設定ファイルを他のシステム(非Qtアプリケーション、ウェブサービスなど)と共有する必要がある場合。
  • 設定が非常に複雑で、ツリー構造や配列などのリッチなデータ構造を必要とする場合。

例 (JSON を使用したカスタム設定管理)

#include <QCoreApplication>
#include <QFile>
#include <QJsonDocument>
#include <QJsonObject>
#include <QJsonArray>
#include <QDebug>

// カスタム設定を扱うクラス
class CustomJsonSettings
{
public:
    CustomJsonSettings(const QString &filePath) : m_filePath(filePath) {}

    void setValue(const QString &key, const QVariant &value)
    {
        m_settingsMap[key] = value;
    }

    QVariant value(const QString &key, const QVariant &defaultValue = QVariant()) const
    {
        return m_settingsMap.value(key, defaultValue);
    }

    bool save()
    {
        QFile saveFile(m_filePath);
        if (!saveFile.open(QIODevice::WriteOnly | QIODevice::Text | QIODevice::Truncate)) {
            qWarning() << "ファイルを開けません:" << saveFile.errorString();
            return false;
        }

        QJsonObject rootObject;
        for (auto it = m_settingsMap.constBegin(); it != m_settingsMap.constEnd(); ++it) {
            // QSettings の階層化されたキー (例: "group/key") を JSON オブジェクトの階層に変換
            // 簡単のため、ここでは直接キー名として使用
            rootObject[it.key()] = QJsonValue::fromVariant(it.value());
        }

        QJsonDocument saveDoc(rootObject);
        saveFile.write(saveDoc.toJson());
        saveFile.close();
        return true;
    }

    bool load()
    {
        QFile loadFile(m_filePath);
        if (!loadFile.open(QIODevice::ReadOnly | QIODevice::Text)) {
            qWarning() << "ファイルを開けません:" << loadFile.errorString();
            return false;
        }

        QByteArray data = loadFile.readAll();
        QJsonDocument loadDoc = QJsonDocument::fromJson(data);
        loadFile.close();

        if (loadDoc.isNull() || !loadDoc.isObject()) {
            qWarning() << "JSONのパースに失敗しました。";
            return false;
        }

        m_settingsMap.clear();
        QJsonObject rootObject = loadDoc.object();
        for (auto it = rootObject.constBegin(); it != rootObject.constEnd(); ++it) {
            m_settingsMap[it.key()] = it.value().toVariant();
        }
        return true;
    }

private:
    QString m_filePath;
    QMap<QString, QVariant> m_settingsMap; // 内部で設定を保持するマップ
};

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

    CustomJsonSettings jsonSettings("my_app_settings.json");

    // 設定を書き込む
    jsonSettings.setValue("user/name", "ジェミニ");
    jsonSettings.setValue("app/theme", "dark_mode");
    jsonSettings.setValue("last_session/width", 1024);
    jsonSettings.setValue("last_session/height", 768);

    if (jsonSettings.save()) {
        qDebug() << "設定がJSONファイルに保存されました。";
    }

    // 設定を読み込む
    CustomJsonSettings loadedSettings("my_app_settings.json");
    if (loadedSettings.load()) {
        qDebug() << "設定がJSONファイルから読み込まれました。";
        qDebug() << "User Name:" << loadedSettings.value("user/name").toString();
        qDebug() << "App Theme:" << loadedSettings.value("app/theme").toString();
        qDebug() << "Last Session Width:" << loadedSettings.value("last_session/width").toInt();
        qDebug() << "Last Session Height:" << loadedSettings.value("last_session/height").toInt();
    }

    return a.exec();
}

設定管理用のカスタムクラスを設計する

設定を管理するためだけの専用のC++クラスを設計し、そのクラス内でデータの読み書き、シリアライズ/デシリアライズロジックをカプセル化する方法です。

  • 単純な構造体/クラス
    設定項目を保持する単純な構造体やクラスを作成し、そのインスタンスをバイナリ、JSON、XMLなどでファイルに直接書き込む、あるいはデータベースに保存するなど。
  • Q_PROPERTY とメタオブジェクトシステム
    QObject を継承し、Q_PROPERTY を使用して設定項目をプロパティとして公開します。これにより、QMLやQtのメタオブジェクトシステムを通じて設定にアクセスできます。シリアライズ/デシリアライズは、各プロパティを手動で処理するか、XML/JSONライブラリと組み合わせて行うことになります。

いつ使うべきか

  • 設定の変更通知(Q_PROPERTYNOTIFY シグナルなど)が必要な場合。
  • QMLとの連携を考えている場合。
  • 設定項目のグループ化や依存関係が明確な場合。
  • 設定項目が非常に多く、型安全なアクセスを優先したい場合。
#include <QCoreApplication>
#include <QFile>
#include <QDataStream>
#include <QDebug>
#include <QPoint> // 例としてQPointを保存

// 設定を保持するクラス
class ApplicationSettings
{
public:
    ApplicationSettings()
        : m_windowSize(1024, 768), m_userName("DefaultUser"), m_autoSaveEnabled(true) {}

    QSize windowSize() const { return m_windowSize; }
    void setWindowSize(const QSize &size) { m_windowSize = size; }

    QString userName() const { return m_userName; }
    void setUserName(const QString &name) { m_userName = name; }

    bool autoSaveEnabled() const { return m_autoSaveEnabled; }
    void setAutoSaveEnabled(bool enabled) { m_autoSaveEnabled = enabled; }

    // QDataStreamでのシリアライズ
    void save(QDataStream &out) const
    {
        out << m_windowSize << m_userName << m_autoSaveEnabled;
    }

    // QDataStreamでのデシリアライズ
    void load(QDataStream &in)
    {
        in >> m_windowSize >> m_userName >> m_autoSaveEnabled;
    }

private:
    QSize m_windowSize;
    QString m_userName;
    bool m_autoSaveEnabled;
};

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

    QString settingsFilePath = "app_settings.dat";

    // 設定を書き込む
    ApplicationSettings settingsToSave;
    settingsToSave.setWindowSize(QSize(1280, 800));
    settingsToSave.setUserName("新しいユーザー");
    settingsToSave.setAutoSaveEnabled(false);

    QFile outFile(settingsFilePath);
    if (outFile.open(QIODevice::WriteOnly)) {
        QDataStream out(&outFile);
        out.setVersion(QDataStream::Qt_6_0); // Qtのバージョンを指定 (互換性のため重要)
        settingsToSave.save(out);
        outFile.close();
        qDebug() << "設定がバイナリファイルに保存されました。";
    } else {
        qWarning() << "ファイルを開けません:" << outFile.errorString();
    }

    // 設定を読み込む
    ApplicationSettings loadedSettings;
    QFile inFile(settingsFilePath);
    if (inFile.open(QIODevice::ReadOnly)) {
        QDataStream in(&inFile);
        in.setVersion(QDataStream::Qt_6_0); // 書き込み時と同じバージョンを指定
        loadedSettings.load(in);
        inFile.close();
        qDebug() << "設定がバイナリファイルから読み込まれました。";

        qDebug() << "Window Size:" << loadedSettings.windowSize();
        qDebug() << "User Name:" << loadedSettings.userName();
        qDebug() << "Auto Save Enabled:" << loadedSettings.autoSaveEnabled();
    } else {
        qWarning() << "ファイルを開けません:" << inFile.errorString();
    }

    return a.exec();
}
  • カスタム設定クラス
    • アプリケーションの規模が大きく、設定項目が多い場合に、コードの可読性と保守性を高めたい場合。
    • 設定へのアクセスを型安全にしたい場合。
    • 設定の変更に応じてGUIを更新するなどのリアクティブな挙動が必要な場合。
  • 完全にカスタムなファイルI/O
    • QSettings のキー/値ペアの抽象化が不適切と感じるほど、設定データが複雑で構造化されている場合。
    • 設定ファイルがアプリケーションだけでなく、他のツールやシステムによっても直接扱われる必要がある場合。
    • JSONやXMLなど、業界標準のデータ交換フォーマットを使用したい場合。
  • QSettings::WriteFunc の出番
    • 既存の非標準的な設定ファイル形式をQtアプリケーションで読み書きする必要がある場合。
    • 特定の暗号化、圧縮、あるいは非常に特殊なバイナリレイアウトなど、QSettings の組み込みフォーマットでは実現できない独自の要件がある場合。
    • ただし、QSettings のAPIの恩恵(グループ、配列、キー/値アクセス)を受けたい場合。
  • 最も簡単で一般的
    ほとんどのアプリケーションでは、QSettings::NativeFormat または QSettings::IniFormat で十分です。まずこれを検討してください。