Qt QSettings::fallbacksEnabled()徹底解説:設定値の読み込み優先順位を理解する

2025-05-27

QSettings クラスは、アプリケーションの設定を永続的に保存および読み込むための Qt のメカニズムです。ユーザーの特定のアプリケーション設定、ユーザー全体のアプリケーション設定、システム全体のアプリケーション設定、システム全体の全アプリケーション設定など、複数の異なる場所に設定を保存し、検索することができます。

fallbacksEnabled() メソッドは、この フォールバックメカニズムが有効になっているかどうか を返します。

フォールバックメカニズムとは?

QSettings が設定値を検索する際、通常は以下の順序で設定ファイルやレジストリのエントリを検索します。

  1. ユーザー固有の、特定のアプリケーションの設定場所
  2. ユーザー固有の、組織全体の(すべてのアプリケーションの)設定場所
  3. システム全体の、特定のアプリケーションの設定場所
  4. システム全体の、組織全体の(すべてのアプリケーションの)設定場所

この検索順序は、より具体的な設定(ユーザー個別のアプリケーション設定)が、より一般的な設定(システム全体の組織設定)を上書きすることを可能にします。もし最初の場所で設定値が見つからなかった場合、QSettings は次の場所を「フォールバック」として検索し、設定値を見つけるまでこのプロセスを続けます。

fallbacksEnabled() の役割

bool QSettings::fallbacksEnabled() const は、このフォールバックメカニズムが現在有効になっているかどうかをブール値 (true または false) で返します。

  • false が返される場合:QSettings はフォールバック検索を行いません。設定値が指定されたメインの場所で見つからない場合、デフォルト値(または null の QVariant)が返されます。
  • true が返される場合:QSettings は上記のようなフォールバック検索順序で設定値を検索します。

フォールバックを無効にする理由

通常、フォールバックメカニズムはアプリケーション設定の柔軟性を高めるために便利ですが、特定の状況では無効にしたい場合があります。例えば:

  • 厳密な設定管理
    特定の場所にある設定のみを読み込みたい場合。例えば、ユーザー固有の設定のみを読み込み、システム全体の設定が意図せず適用されるのを防ぎたい場合などです。特にmacOSなどでは、setFallbacksEnabled(false) を呼び出すことで、すべてのアプリケーションに適用されるグローバル設定が読み込まれるのを防ぐことができます。
  • パフォーマンスの向上
    検索する場所が少なくなるため、設定の読み込みが高速になる可能性があります。


QSettings::fallbacksEnabled() はフォールバックメカニズムが有効かどうかを問い合わせるメソッドであり、それ自体が直接エラーを引き起こすことは稀です。しかし、このフォールバックメカニズムの動作を理解していないことによって、意図しない設定値の読み込みや書き込みが発生することがあります。

想定外の設定値が読み込まれる

問題の症状
QSettings::value() を呼び出したときに、期待していた値とは異なる値が返される。特に、ユーザー固有の設定を変更したはずなのに、アプリケーションを再起動すると古い設定に戻ってしまうように見える場合など。

原因
fallbacksEnabled()true (デフォルト) であるため、QSettings は複数の場所を検索します。期待する場所(例: ユーザー固有のアプリケーション設定)に設定値が見つからなかった場合、QSettings はより一般的な場所(例: システム全体のアプリケーション設定や組織全体のアプリケーション設定)から設定値を読み取ってしまうことがあります。

トラブルシューティング

  • 各設定場所を直接確認する
    • Windows
      レジストリエディタ (regedit) を使用して、HKEY_CURRENT_USERHKEY_LOCAL_MACHINE の下の Qt 関連のパス(通常は Software\<OrganizationName>\<ApplicationName>)を確認します。
    • macOS
      ~/Library/Preferences//Library/Preferences/ 内の .plist ファイルを確認します。
    • Linux/Unix
      $HOME/.config//etc/xdg/ 内の .conf または .ini ファイルを確認します。
  • QSettings::setFallbacksEnabled(false) を試す
    一時的にフォールバックを無効にし、設定値が期待通りに読み込まれるかを確認します。これにより、問題がフォールバックメカニズムによるものかどうかの切り分けができます。もしフォールバックを無効にすると期待通りの値が読み込まれるのであれば、別の場所に古い、または意図しない設定値が存在している可能性が高いです。
  • QSettings::scope() と QSettings::format() を確認する
    QSettings オブジェクトの生成時に指定したスコープ(QSettings::UserScope または QSettings::SystemScope)とフォーマット(QSettings::NativeFormat または QSettings::IniFormat)が意図通りか確認します。これらが異なると、設定の保存場所も異なります。
  • QSettings::fileName() を確認する
    実際にどのファイルから設定が読み込まれているかを確認するために、QSettings::fileName() を使用して現在の QSettings オブジェクトが関連付けられているファイルのパスを取得します。これにより、設定がどの場所にあるのかを把握できます。

設定が保存されない、または変更が反映されない

問題の症状
QSettings::setValue() で設定値を変更しても、アプリケーションを再起動すると元の値に戻ってしまう。

原因

  • 書き込み権限の問題
    設定ファイルの保存場所にアプリケーションが書き込み権限を持っていない場合、設定は保存されません。これは特に QSettings::SystemScope を使用する場合や、INIファイルが書き込み禁止になっている場合に発生しやすいです。
  • QSettings::sync() の呼び忘れ
    setValue() で設定値を変更した後、変更を永続ストレージにすぐに書き込むには QSettings::sync() を呼び出す必要があります。QSettings オブジェクトが破棄されるとき(スタック上のオブジェクトがスコープを抜けるときなど)に自動的に sync() が呼ばれますが、明示的に呼び出さないと、アプリケーションのクラッシュなどで変更が失われる可能性があります。
  • 誤った QSettings オブジェクトへの書き込み
    ユーザーが変更したい設定の場所(例: ユーザー固有の設定)とは異なる QSettings オブジェクト(例: システム設定を指すオブジェクト)に対して setValue() を呼び出している可能性があります。フォールバックが有効な場合、読み込み時にはシステム設定がフォールバックとして使用されるため、ユーザー固有の設定が変更されていないと、次回起動時にシステム設定が読み込まれてしまいます。

トラブルシューティング

  • エラー状態の確認
    QSettings::status() メソッドを呼び出して、QSettings::AccessErrorQSettings::FormatError などのエラーが発生していないかを確認します。
  • setValue() 後に sync() を呼び出す
    変更をすぐにディスクに書き込みたい場合は、必ず settings.sync(); を呼び出すようにします。
  • QSettings オブジェクトのスコープとフォーマットを確認する
    QSettings オブジェクトをどのようにインスタンス化しているかを確認します。例えば、QSettings(QSettings::UserScope, organization, application) のように、意図するスコープとフォーマットで作成されていることを確認します。

プラットフォーム間の挙動の違い

問題の症状
あるOSでは期待通りに動作するが、別のOSでは設定が読み書きできない、または意図しない場所から読み込まれる。

原因
QSettings はプラットフォームごとに異なるデフォルトの保存場所を使用します。fallbacksEnabled() の動作自体は同じですが、各プラットフォームでのフォールバックパスの具体的な順序と場所が異なるため、設定ファイルの配置や権限の考慮が必要です。

  • QSettings::setPath() を使用してパスを明示的に指定する(慎重に)
    特定の場所にある設定ファイルにアクセスしたい場合は、QSettings::setPath() を使用して、指定したフォーマットとスコープのパスを上書きできます。ただし、これを乱用するとプラットフォーム依存のコードになり、ポータビリティが低下する可能性があるため、注意が必要です。
  • QCoreApplication::setOrganizationName() と QCoreApplication::setApplicationName() の設定
    QSettings を使用する前に、QCoreApplication::setOrganizationName()QCoreApplication::setApplicationName() を適切に設定していることを確認します。これらは設定ファイルのパスを決定する上で非常に重要です。
  • 各プラットフォームのデフォルトパスを理解する
    Qt のドキュメントで、Windows、macOS、Linux/Unix における QSettings のデフォルトの保存場所を確認します。


QSettings::fallbacksEnabled() は、QSettings のフォールバックメカニズムが有効であるかを確認するためのメソッドです。このフォールバックメカニズムが、どのように設定値の読み込みに影響を与えるかを示す具体的な例を挙げます。

前提
QSettings は、アプリケーション名と組織名に基づいて設定ファイルを検索します。例示のために、これらの名前を事前に設定します。

#include <QCoreApplication>
#include <QSettings>
#include <QDebug>
#include <QStandardPaths> // 設定ファイルのパスを確認するために使用

// アプリケーションの初期設定
void setupApplicationDefaults() {
    QCoreApplication::setOrganizationName("MyOrg");
    QCoreApplication::setOrganizationDomain("myorg.com"); // オプションだが推奨
    QCoreApplication::setApplicationName("MyApp");
}

// ヘルパー関数:現在のQSettingsオブジェクトの関連パスを表示
void printSettingsPaths(const QSettings& settings) {
    qDebug() << "--- Current QSettings Paths ---";
    qDebug() << "Format:" << settings.format();
    qDebug() << "Scope:" << settings.scope();
    qDebug() << "File Name (active):" << settings.fileName();

    // QStandardPathsを使って一般的な設定パスも確認
    qDebug() << "User config location:" << QStandardPaths::writableLocation(QStandardPaths::AppConfigLocation);
    qDebug() << "System config location:" << QStandardPaths::writableLocation(QStandardPaths::AppConfigLocation, QStandardPaths::SystemLocation);
    qDebug() << "-------------------------------";
}

例1: フォールバックが有効な場合の動作(デフォルト)

この例では、ユーザー設定とシステム設定の両方に同じキーで異なる値を設定し、フォールバックが有効な場合にどちらの値が読み込まれるかを示します。

シナリオ

  1. システム設定として myKey = "system_value" を保存。
  2. ユーザー設定として myKey = "user_value" を保存。
  3. QSettingsmyKey を読み込む。フォールバックが有効な場合、ユーザー設定が優先されるはずです。
  4. ユーザー設定を削除し、再度 myKey を読み込む。今度はシステム設定がフォールバックとして読み込まれるはずです。
int main(int argc, char *argv[]) {
    QCoreApplication a(argc, argv);
    setupApplicationDefaults();

    qDebug() << "--- 例1: フォールバックが有効な場合の動作 ---";

    // 1. システム設定への書き込み (管理者権限が必要な場合があります)
    // 実際には、管理者権限のないアプリケーションからシステム設定に直接書き込むのは推奨されません。
    // デプロイ時にインストールスクリプトなどで行うべきです。
    // 例のため、ここでは直接書き込みを試みますが、失敗する可能性があります。
    QSettings systemSettings(QSettings::SystemScope, QSettings::NativeFormat,
                             QCoreApplication::organizationName(), QCoreApplication::applicationName());
    systemSettings.setValue("myKey", "system_value");
    systemSettings.sync(); // 変更をすぐに保存
    qDebug() << "システム設定に 'myKey' = '" << systemSettings.value("myKey").toString() << "' を書き込みました。";
    // printSettingsPaths(systemSettings); // パスを確認したい場合

    // 2. ユーザー設定への書き込み
    QSettings userSettings(QSettings::UserScope, QSettings::NativeFormat,
                           QCoreApplication::organizationName(), QCoreApplication::applicationName());
    userSettings.setValue("myKey", "user_value");
    userSettings.sync(); // 変更をすぐに保存
    qDebug() << "ユーザー設定に 'myKey' = '" << userSettings.value("myKey").toString() << "' を書き込みました。";
    // printSettingsPaths(userSettings); // パスを確認したい場合

    // 3. QSettings オブジェクトを作成し、myKey を読み込む (デフォルトで fallbacksEnabled() は true)
    QSettings settings; // デフォルトではユーザー・スコープ、ネイティブ・フォーマット
    qDebug() << "\nQSettings::fallbacksEnabled():" << settings.fallbacksEnabled(); // デフォルトは true

    QString value = settings.value("myKey").toString();
    qDebug() << "ユーザー設定とシステム設定が存在する場合、'myKey' の値は: '" << value << "'";
    // 期待される出力: "user_value" (ユーザー設定が優先されるため)

    // 4. ユーザー設定を削除し、フォールバックの動作を確認
    qDebug() << "\nユーザー設定を削除し、再度読み込みます...";
    userSettings.remove("myKey"); // ユーザー設定から myKey を削除
    userSettings.sync();
    qDebug() << "ユーザー設定から 'myKey' を削除しました。";

    // 再度読み込み
    value = settings.value("myKey").toString();
    qDebug() << "ユーザー設定が削除された後、'myKey' の値は: '" << value << "'";
    // 期待される出力: "system_value" (システム設定がフォールバックとして読み込まれるため)

    return 0; // QCoreApplication::exec() は不要 (バッチ的な処理のため)
}

実行結果の例 (Windows レジストリの場合)

--- 例1: フォールバックが有効な場合の動作 ---
システム設定に 'myKey' = 'system_value' を書き込みました。
ユーザー設定に 'myKey' = 'user_value' を書き込みました。

QSettings::fallbacksEnabled(): true
ユーザー設定とシステム設定が存在する場合、'myKey' の値は: 'user_value'

ユーザー設定を削除し、再度読み込みます...
ユーザー設定から 'myKey' を削除しました。
ユーザー設定が削除された後、'myKey' の値は: 'system_value'

例2: フォールバックを無効にした場合の動作

この例では、QSettings::setFallbacksEnabled(false) を使用してフォールバックを無効にした場合、どのように設定値の読み込みが変わるかを示します。

シナリオ

  1. システム設定とユーザー設定は例1と同じ状態(system_valueuser_value が存在)。
  2. QSettings でフォールバックを無効にし、myKey を読み込む。ユーザー設定のみが考慮されるはずです。
  3. ユーザー設定を削除し、再度読み込む。フォールバックが無効なので、何も見つからず、デフォルト値(空の文字列)が返されるはずです。
int main(int argc, char *argv[]) {
    QCoreApplication a(argc, argv);
    setupApplicationDefaults();

    qDebug() << "\n--- 例2: フォールバックを無効にした場合の動作 ---";

    // 事前準備: 例1と同様にシステム設定とユーザー設定が存在するようにする
    QSettings systemSettings(QSettings::SystemScope, QSettings::NativeFormat,
                             QCoreApplication::organizationName(), QCoreApplication::applicationName());
    systemSettings.setValue("myKey", "system_value");
    systemSettings.sync();

    QSettings userSettings(QSettings::UserScope, QSettings::NativeFormat,
                           QCoreApplication::organizationName(), QCoreApplication::applicationName());
    userSettings.setValue("myKey", "user_value");
    userSettings.sync();

    // QSettings オブジェクトを作成し、フォールバックを無効にする
    QSettings settings; // デフォルトではユーザー・スコープ、ネイティブ・フォーマット
    settings.setFallbacksEnabled(false); // フォールバックを無効化
    qDebug() << "QSettings::fallbacksEnabled():" << settings.fallbacksEnabled(); // false になっていることを確認

    QString value = settings.value("myKey").toString();
    qDebug() << "フォールバック無効時、ユーザー設定とシステム設定が存在する場合、'myKey' の値は: '" << value << "'";
    // 期待される出力: "user_value" (ユーザー設定は読み込まれる)

    // ユーザー設定を削除
    qDebug() << "\nユーザー設定を削除し、再度読み込みます...";
    userSettings.remove("myKey");
    userSettings.sync();
    qDebug() << "ユーザー設定から 'myKey' を削除しました。";

    // フォールバック無効のまま再度読み込み
    value = settings.value("myKey").toString();
    qDebug() << "フォールバック無効時、ユーザー設定が削除された後、'myKey' の値は: '" << value << "'";
    // 期待される出力: "" (空文字列、何も見つからないため)

    return 0;
}

実行結果の例 (Windows レジストリの場合)

--- 例2: フォールバックを無効にした場合の動作 ---
QSettings::fallbacksEnabled(): false
フォールバック無効時、ユーザー設定とシステム設定が存在する場合、'myKey' の値は: 'user_value'

ユーザー設定を削除し、再度読み込みます...
ユーザー設定から 'myKey' を削除しました。
フォールバック無効時、ユーザー設定が削除された後、'myKey' の値は: ''

これらの例からわかるように、QSettings::fallbacksEnabled() は、QSettings::value() が設定値を検索する際に、複数の設定場所を考慮するかどうかを制御します。

  • false
    指定されたスコープ(通常はユーザー)の場所のみを検索し、他の場所をフォールバックとして使用しません。
  • true (デフォルト)
    ユーザー設定が見つからない場合に、より一般的な(システムなどの)設定を検索します。


しかし、このフォールバックメカニズムの代わりに、または組み合わせて、より柔軟な設定管理を実現するための方法はいくつか存在します。

複数の QSettings オブジェクトを明示的に使用する

これは、fallbacksEnabled(false) と似ていますが、異なるスコープやフォーマットの QSettings オブジェクトを複数作成し、明示的にそれらを順に検索する方法です。これにより、検索順序とどの設定を読み込むかを完全に制御できます。

アプローチ

  1. ユーザー固有の設定を読み込む QSettings オブジェクトを作成します。
  2. システム全体の設定を読み込む QSettings オブジェクトを作成します。
  3. 設定値を読み込む際、まずユーザー設定のオブジェクトで value() を試みます。
  4. もし値が見つからなかった場合(QVariant が無効であるか、指定したデフォルト値が返された場合)、次にシステム設定のオブジェクトで value() を試みます。

利点

  • 書き込み対象の場所を厳密に指定できます(例: ユーザー設定には書き込めるが、システム設定には書き込めない)。
  • 各設定オブジェクトの fileName() を確認することで、どの設定ファイルが実際に使用されているかを明確に把握できます。
  • 検索順序と優先順位をコードで完全に制御できます。

欠点

  • 設定のロジックが複雑になる場合があります。
  • 設定値を読み込むたびに複数の QSettings オブジェクトにアクセスするコードが必要になり、冗長になる可能性があります。

コード例

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

void setupApplicationDefaults() {
    QCoreApplication::setOrganizationName("MyOrg");
    QCoreApplication::setApplicationName("MyApp");
}

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

    qDebug() << "--- 複数の QSettings オブジェクトを明示的に使用する ---";

    // 1. システム設定を準備 (存在しない場合は作成)
    QSettings systemSettings(QSettings::SystemScope, QSettings::NativeFormat,
                             QCoreApplication::organizationName(), QCoreApplication::applicationName());
    if (!systemSettings.contains("fontSize")) {
        systemSettings.setValue("fontSize", 12);
        systemSettings.sync();
        qDebug() << "システム設定に fontSize = 12 を書き込みました。";
    }
    // systemSettings.fileName() を表示してパスを確認することもできます

    // 2. ユーザー設定を準備 (存在しない場合は作成)
    QSettings userSettings(QSettings::UserScope, QSettings::NativeFormat,
                           QCoreApplication::organizationName(), QCoreApplication::applicationName());
    if (!userSettings.contains("fontSize")) {
        userSettings.setValue("fontSize", 10);
        userSettings.sync();
        qDebug() << "ユーザー設定に fontSize = 10 を書き込みました。";
    }
    // userSettings.fileName() を表示してパスを確認することもできます

    // 3. 設定値を読み込む(ユーザー設定を優先)
    int fontSize = userSettings.value("fontSize", -1).toInt(); // まずユーザー設定を試す

    if (fontSize == -1) { // ユーザー設定で見つからなかった場合
        qWarning() << "ユーザー設定に 'fontSize' が見つかりませんでした。システム設定を試します。";
        fontSize = systemSettings.value("fontSize", 10).toInt(); // システム設定を試す
    }

    qDebug() << "読み込まれた fontSize の値:" << fontSize;
    // userSettings.remove("fontSize"); // ユーザー設定を削除して動作を試すこともできます

    // ユーザー設定が存在する場合(例: 10 が出力される)
    // ユーザー設定を削除した場合(例: 12 が出力される)

    // QSettings::fallbacksEnabled() の現在の状態を確認 (この場合は意味がないが、参考として)
    QSettings defaultSettings;
    qDebug() << "デフォルトの QSettings オブジェクトの fallbacksEnabled():" << defaultSettings.fallbacksEnabled();


    return 0;
}

特定のファイルパスやレジストリパスを指定して QSettings をインスタンス化する

QSettings には、組織名やアプリケーション名に依存しない、直接ファイルパスやレジストリパスを指定してインスタンス化するコンストラクタがあります。これにより、アプリケーションが特定のファイルから設定を読み書きするように強制できます。

アプローチ

  1. 設定を保存したいINIファイルなどのパスを決定します。
  2. QSettings settings("path/to/my/settings.ini", QSettings::IniFormat); のように、直接パスとフォーマットを指定して QSettings オブジェクトを作成します。

利点

  • ポータブルなアプリケーション(INIファイルなどを実行ファイルと同じディレクトリに配置する場合など)に特に適しています。
  • フォールバックメカニズムが完全にバイパスされます(指定されたファイルのみが対象)。
  • 設定ファイルの場所を完全に制御できます。

欠点

  • 複数の異なる種類の(例: ユーザーとシステム)設定を管理する場合、複数の異なるファイルを作成し、それぞれの QSettings オブジェクトを管理する必要があります。
  • プラットフォーム固有のデフォルトパス(レジストリやXDGディレクトリなど)を使用したい場合には不向きです。

コード例

#include <QCoreApplication>
#include <QSettings>
#include <QDebug>
#include <QDir> // ファイルパス操作のため

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

    qDebug() << "--- 特定のファイルパスを直接指定する ---";

    QString settingsFilePath = QCoreApplication::applicationDirPath() + "/my_custom_settings.ini";
    qDebug() << "設定ファイルのパス:" << settingsFilePath;

    // 特定のINIファイルを指定してQSettingsを作成
    QSettings customFileSettings(settingsFilePath, QSettings::IniFormat);

    // 値を書き込む
    customFileSettings.setValue("windowSize", QSize(800, 600));
    customFileSettings.setValue("theme", "Dark");
    customFileSettings.sync();
    qDebug() << "カスタムINIファイルに設定を書き込みました。";

    // 値を読み込む
    QSize size = customFileSettings.value("windowSize", QSize(640, 480)).toSize();
    QString theme = customFileSettings.value("theme", "Light").toString();
    qDebug() << "読み込まれた windowSize:" << size;
    qDebug() << "読み込まれた theme:" << theme;

    // QSettings::fallbacksEnabled() の状態を確認 (このオブジェクトでは常に false のような振る舞いになる)
    qDebug() << "customFileSettings の fallbacksEnabled():" << customFileSettings.fallbacksEnabled();
    // 実際には、このコンストラクタを使用した場合、fallbacksEnabled() は常に false を返すか、
    // またはその値が意味を持たない(単一のファイルのみを扱うため)振る舞いとなります。

    return 0;
}

独自のカスタム設定管理クラスを実装する

最も柔軟な方法ですが、実装の手間がかかります。QSettings の機能を抽象化し、複数の設定ソース(QSettings、XMLファイル、データベースなど)から設定を読み書きする独自のクラスを構築します。

アプローチ

  1. SettingsManager のようなクラスを作成します。
  2. このクラス内に、異なるスコープやフォーマットの複数の QSettings オブジェクト(または他の設定ストレージメカニズム)を保持します。
  3. getValue(const QString& key)setValue(const QString& key, const QVariant& value) のようなメソッドを提供し、内部で優先順位に基づいて適切な設定ソースにアクセスします。

利点

  • テストが容易になります。
  • 非常に複雑なフォールバックロジックや、複数の設定ソース(ローカルファイル、ネットワーク設定、データベースなど)を組み合わせた設定管理を実現できます。
  • 設定管理ロジックをアプリケーションの他の部分から完全に分離できます。

欠点

  • 既存の QSettings の利便性(プラットフォーム固有のパスの自動解決など)を一部失う可能性があります。
  • 実装の複雑さが増し、コード量が増えます。
#include <QCoreApplication>
#include <QSettings>
#include <QDebug>
#include <QMap>

// 独自のカスタム設定管理クラス
class MySettingsManager {
public:
    MySettingsManager() {
        // ユーザー固有設定
        userSettings = new QSettings(QSettings::UserScope, QSettings::NativeFormat,
                                     QCoreApplication::organizationName(),
                                     QCoreApplication::applicationName(), this);
        userSettings->setFallbacksEnabled(false); // このオブジェクトはフォールバックしない

        // システム全体設定
        systemSettings = new QSettings(QSettings::SystemScope, QSettings::NativeFormat,
                                       QCoreApplication::organizationName(),
                                       QCoreApplication::applicationName(), this);
        systemSettings->setFallbacksEnabled(false); // このオブジェクトもフォールバックしない

        // (オプション) デフォルト設定 (コード内にハードコードするか、別のファイルから読み込む)
        defaultValues["defaultTheme"] = "Light";
        defaultValues["defaultFontSize"] = 10;
    }

    QVariant value(const QString& key, const QVariant& defaultValue = QVariant()) const {
        // ユーザー設定を最優先
        QVariant val = userSettings->value(key);
        if (val.isValid()) {
            qDebug() << "Key" << key << "found in user settings.";
            return val;
        }

        // 次にシステム設定を試す
        val = systemSettings->value(key);
        if (val.isValid()) {
            qDebug() << "Key" << key << "found in system settings (fallback).";
            return val;
        }

        // 最後にデフォルト値または提供された defaultValue を返す
        if (defaultValues.contains(key)) {
            qDebug() << "Key" << key << "found in internal defaults (fallback).";
            return defaultValues.value(key);
        }

        qDebug() << "Key" << key << "not found anywhere. Using provided default.";
        return defaultValue;
    }

    void setValue(const QString& key, const QVariant& value) {
        // 設定は常にユーザー設定に書き込む
        userSettings->setValue(key, value);
        userSettings->sync();
        qDebug() << "Key" << key << "written to user settings.";
    }

private:
    QSettings* userSettings;
    QSettings* systemSettings;
    QMap<QString, QVariant> defaultValues;
};

int main(int argc, char *argv[]) {
    QCoreApplication a(argc, argv);
    setupApplicationDefaults(); // QCoreApplication::setOrganizationName など

    MySettingsManager manager;

    // システム設定に値を書き込む (テスト用、通常はデプロイ時に行う)
    QSettings tempSystemSettings(QSettings::SystemScope, QSettings::NativeFormat,
                                 QCoreApplication::organizationName(), QCoreApplication::applicationName());
    tempSystemSettings.setValue("theme", "Dark");
    tempSystemSettings.setValue("language", "en_US");
    tempSystemSettings.sync();

    // ユーザー設定に値を書き込む
    manager.setValue("theme", "Blue"); // ユーザーがテーマを青に設定
    manager.setValue("fontSize", 14); // ユーザーがフォントサイズを14に設定

    // 設定値を読み込む
    qDebug() << "Theme:" << manager.value("theme").toString();        // "Blue" (ユーザー設定優先)
    qDebug() << "FontSize:" << manager.value("fontSize").toInt();    // 14 (ユーザー設定優先)
    qDebug() << "Language:" << manager.value("language").toString();  // "en_US" (ユーザー設定になく、システム設定にある)
    qDebug() << "Default Theme:" << manager.value("defaultTheme").toString(); // "Light" (内部デフォルト値)
    qDebug() << "NonExistentKey:" << manager.value("nonExistentKey", "DefaultFallback").toString(); // "DefaultFallback"

    // ユーザー設定を削除して、動作を確認
    qDebug() << "\nユーザー設定を削除して再テスト...";
    QSettings userRemoveSettings(QSettings::UserScope, QSettings::NativeFormat,
                                 QCoreApplication::organizationName(), QCoreApplication::applicationName());
    userRemoveSettings.remove("theme");
    userRemoveSettings.remove("fontSize");
    userRemoveSettings.sync();

    qDebug() << "Theme after user settings removal:" << manager.value("theme").toString(); // "Dark" (システム設定が読み込まれる)
    qDebug() << "FontSize after user settings removal:" << manager.value("fontSize").toInt(); // 10 (内部デフォルト値)

    return 0;
}