QSettings::SettingsMapだけじゃない!Qt設定保存の代替手段と選び方
QSettings::SettingsMap とは
QSettings::SettingsMap
は、Qtフレームワークの QSettings
クラス内で定義されている型エイリアス(typedef)です。これは実際には QMap<QString, QVariant>
のことです。
QMap<QString, QVariant>
: キーと値のペアを格納するためのコンテナクラスです。QString
: 設定項目の「キー」(名前)を表します。例えば、「ウィンドウの幅」や「ユーザー名」など、設定を一意に識別するための文字列です。QVariant
: 設定項目の「値」を表します。QVariant
は、Qtの多くのデータ型(整数、文字列、ブール値、QSize、QRectなど)を汎用的に扱うことができる型です。これにより、異なる種類のデータを単一のコンテナに格納できます。
QSettings クラスと SettingsMap の関係
QSettings
クラスは、アプリケーションの設定を永続的に保存および読み込むための、プラットフォーム非依存のインターフェースを提供します。Windowsではレジストリ、macOSではplistファイル、Unix/LinuxではINIファイルなど、OSネイティブな方法で設定を管理してくれます。
通常、QSettings
を直接使用する場合、setValue()
メソッドでキーと値を設定し、value()
メソッドで値を読み出します。
QSettings settings("MyCompany", "MyApplication");
settings.setValue("editor/wrapMargin", 80);
int margin = settings.value("editor/wrapMargin", 68).toInt();
QSettings::SettingsMap
は、主に QSettings
のカスタムストレージ形式を実装する際に内部的に利用されます。
QSettings
は、INIファイル形式やネイティブな設定形式以外にも、独自の形式で設定を保存・読み込みする機能を提供しています。このカスタム形式を登録する際には、QSettings::registerFormat()
関数を使用し、設定の読み込みと書き込みを行うカスタム関数を渡します。
これらのカスタム関数のシグネチャは以下のようになります。
bool readFunc(QIODevice &device, QSettings::SettingsMap &map);
bool writeFunc(QIODevice &device, const QSettings::SettingsMap &map);
ここで QSettings::SettingsMap
が登場します。
-
writeFunc
:- この関数は、
QSettings
クラスが内部的に持っている設定データ(map
引数として渡される)を、指定されたQIODevice
に書き込みます。 map
には、アプリケーションがsetValue()
で設定したすべてのキーと値のペアが含まれています。
- この関数は、
-
readFunc
:- この関数は、指定された
QIODevice
(ファイルなど) から設定データを読み込みます。 - 読み込んだデータを、
QSettings::SettingsMap
型のmap
オブジェクトにキーと値のペアとして格納します。 QSettings
クラスは、このmap
からデータを取得し、アプリケーションの他の部分でvalue()
メソッドを通じて利用できるようにします。
- この関数は、指定された
つまり、QSettings::SettingsMap
は、カスタムフォーマットを実装する際に、QSettings
の内部的なデータ構造(キーと値のペアの集合)と、カスタム形式で保存されるデータの間の橋渡しをする役割を担っています。これにより、開発者はファイル形式の解析や生成の部分に集中でき、QSettingsが提供する便利なインターフェース(setValue()
, value()
など)を維持したまま、独自のストレージメカニズムを組み込むことができます。
- カスタムの読み書き関数を実装する際に、
QSettings
とデータのやり取りを行うためのコンテナとして機能する。 - 主に
QSettings
クラスのカスタムストレージ形式を実装する際に、設定データの内部表現として利用される。 QString
がキー、QVariant
が値を表す、設定データのキーと値のペアの集合である。QSettings::SettingsMap
はQMap<QString, QVariant>
の型エイリアスである。
QSettings::SettingsMap
自体が直接エラーの原因になることは稀ですが、これが使われるカスタム設定形式の読み書き関数(QSettings::ReadFunc
および QSettings::WriteFunc
)の実装において、様々な問題が発生する可能性があります。
カスタムフォーマットの読み込み/書き込み関数におけるエラー
QSettings::SettingsMap
は、カスタムフォーマットの readFunc
および writeFunc
関数を通じてQSettingsとデータのやり取りをします。これらの関数の実装に問題があると、設定の保存や読み込みが正しく行われません。
一般的なエラー
- パフォーマンス問題: 巨大な設定ファイルを効率的に処理できない場合、アプリケーションの起動や終了が遅くなる可能性があります。
- 空のキー/値:
SettingsMap
に空のキーや不正な値が格納され、それが原因でファイル形式が崩れたり、パースエラーが発生したりする。 - データ型の不一致:
QVariant
に格納されたデータと、アプリケーションで期待されるデータ型が一致しない場合、value().toInt()
やvalue().toString()
などの変換が失敗する可能性があります。特にカスタム型をQVariant
に格納する場合に発生しやすいです。 - 部分的なデータの読み込み/書き込み:
readFunc
がファイル全体を読み込まなかったり、writeFunc
がSettingsMap
内のすべてのデータを書き込まなかったりする場合、一部の設定が失われたり、正しく読み込まれなかったりします。 - アクセス権エラー(
QSettings::AccessError
):writeFunc
が、書き込み権限のないファイルやディレクトリに書き込もうとした場合に発生します。 - 不正なデータ形式(
QSettings::FormatError
):readFunc
が、予期しない形式のファイルを読み込もうとしたり、不正なデータをパースしようとしたりすると発生します。
トラブルシューティング
- カスタム型の登録:
QVariant
にカスタムのデータ型を格納する場合、そのカスタム型がQ_DECLARE_METATYPE
で宣言され、かつqRegisterMetaType()
およびqRegisterMetaTypeStreamOperators()
(またはQDataStream
のoperator<<
,operator>>
のオーバーロード) で登録されていることを確認します。これを忘れると、QVariant
がカスタム型を適切にシリアライズ/デシリアライズできず、不正なデータが保存されたり、空のデータが読み込まれたりします。- 例
もしこれらが欠けていると、// MyCustomType.h struct MyCustomType { int value1; QString value2; // ... }; Q_DECLARE_METATYPE(MyCustomType) // QVariantに格納するために必要 QDataStream &operator<<(QDataStream &out, const MyCustomType &data); QDataStream &operator>>(QDataStream &in, MyCustomType &data); // MyCustomType.cpp QDataStream &operator<<(QDataStream &out, const MyCustomType &data) { out << data.value1 << data.value2; return out; } QDataStream &operator>>(QDataStream &in, MyCustomType &data) { in >> data.value1 >> data.value2; return in; } // main.cpp など、アプリケーションの起動時 qRegisterMetaType<MyCustomType>("MyCustomType"); // QVariantで型の名前を扱うために必要 // QDataStreamのオペレーターを登録 qRegisterMetaTypeStreamOperators<MyCustomType>("MyCustomType");
QSettings::setValue()
でMyCustomType
を保存しても正しくシリアライズされず、QSettings::value().value<MyCustomType>()
で読み込んでもデフォルト値や不正なデータになってしまうことがあります。
- 例
- テストファイルの利用: 小さな設定ファイルを作成し、それを読み書きするテストコードを書き、問題の切り分けを行います。
- エラー処理の追加:
readFunc
およびwriteFunc
の中で、ファイル読み書きのエラー(QIODevice::error()
)やパースエラーを適切に処理し、false
を返すようにします。 readFunc
/writeFunc
のデバッグ:- これらの関数内にログ出力(
qDebug()
など)を追加し、QIODevice
から読み込まれるバイトデータや、SettingsMap
に格納されるキーと値のペアを詳細に確認します。 - カスタムファイル形式の仕様と、実際に読み書きされるデータが一致しているかを検証します。
- 特に
QVariant
の型変換に注意し、QVariant::type()
やQVariant::canConvert()
を使って予期せぬ型変換が行われていないか確認します。
- これらの関数内にログ出力(
QSettings::status()
の確認:QSettings
オブジェクトのstatus()
メソッドを呼び出して、現在のエラー状態を確認します。QSettings::NoError
: エラーなしQSettings::AccessError
: ファイルアクセスエラーQSettings::FormatError
: フォーマットエラー- これを確認することで、エラーの発生源がファイルアクセスか、データ形式のパースにあるのかを特定できます。
QSettings オブジェクトの初期化に関する問題
QSettings::SettingsMap
は、QSettings
オブジェクトが適切に初期化されて、正しいカスタムフォーマットが登録されていることを前提としています。
一般的なエラー
- 組織名/アプリケーション名の不一致:
QCoreApplication::setOrganizationName()
やQCoreApplication::setApplicationName()
を呼び出す前にQSettings
オブジェクトをデフォルトコンストラクタで作成すると、予期しない場所に設定ファイルが作成されることがあります。これはSettingsMap
自体の問題ではないですが、設定の保存場所が想定と異なるため、読み書きができないという形で現れます。 - ファイルパスの問題: カスタムフォーマットでファイルを指定する場合、そのパスが適切でない(存在しないディレクトリ、権限がないなど)と、ファイルが作成/読み込みできません。
- カスタムフォーマットの未登録:
QSettings::registerFormat()
を呼び出す前に、そのフォーマットを使用するQSettings
オブジェクトを作成してしまうと、デフォルトのフォーマットが使用されてしまい、カスタムフォーマットが適用されません。
- 組織名/アプリケーション名の設定: アプリケーションの起動時に、
QCoreApplication::setOrganizationName()
とQCoreApplication::setApplicationName()
を適切に設定します。これにより、デフォルトのQSettings
コンストラクタが正しいパスで設定ファイルを扱うようになります。 - ファイルパスの確認:
QSettings
コンストラクタで指定したファイルパスや、QSettings::setPath()
で設定したパスが正しいことを確認します。必要に応じてQDir::mkpath()
などでディレクトリを作成します。 registerFormat()
の呼び出し順序:QCoreApplication
オブジェクトが作成された後、かつカスタムフォーマットを使用するQSettings
オブジェクトを作成する前にQSettings::registerFormat()
を呼び出すようにします。通常、main
関数内でアプリケーションの初期化と同時に行います。
- エラーメッセージの不足: カスタムフォーマットの読み書き関数内で、発生した具体的なエラー情報を
QSettings
に通知する標準的なメカニズムは直接提供されていません。デバッグにはログ出力が重要になります。 - スレッドセーフティ:
QSettings
オブジェクトはスレッドセーフですが、カスタムのreadFunc
やwriteFunc
関数は、複数のスレッドから同時にアクセスされる可能性がある場合、開発者が明示的にスレッドセーフティを考慮する必要があります(例:QMutex
を使用するなど)。
以下に、QSettings::SettingsMap
を使用したカスタム設定形式の読み書き関数の例を示します。ここでは、設定をシンプルなプレーンテキストファイルに保存する例を考えます。各設定項目は「キー=値」の形式で1行に記述され、行頭に #
がある場合はコメントとみなします。
例1: プレーンテキスト形式のカスタム設定
この例では、QSettings
のカスタムフォーマットとして、.txtsettings
という拡張子のファイルを定義し、その読み書き関数を実装します。
main.cpp
#include <QCoreApplication>
#include <QSettings>
#include <QDebug>
#include <QMap>
#include <QStringList>
#include <QFile>
#include <QTextStream>
#include <QVariant>
// カスタム設定形式の読み込み関数
// QIODevice からデータを読み込み、QSettings::SettingsMap に格納します
bool readCustomSettings(QIODevice &device, QSettings::SettingsMap &map)
{
QTextStream in(&device);
in.setCodec("UTF-8"); // UTF-8エンコーディングを使用
while (!in.atEnd()) {
QString line = in.readLine().trimmed(); // 行を読み込み、前後の空白を削除
if (line.isEmpty() || line.startsWith("#")) { // 空行またはコメント行はスキップ
continue;
}
int equalsIndex = line.indexOf('=');
if (equalsIndex > 0) {
QString key = line.left(equalsIndex).trimmed();
QString valueStr = line.mid(equalsIndex + 1).trimmed();
// QVariant は多くの型を自動的に推論できますが、
// ここではシンプルにQStringとして格納します。
// 必要に応じて、ここで型変換ロジックを追加できます。
map[key] = valueStr;
} else {
// フォーマットエラーとして処理することも可能ですが、ここでは無視
qDebug() << "Warning: Invalid line format:" << line;
}
}
return true; // 読み込み成功
}
// カスタム設定形式の書き込み関数
// QSettings::SettingsMap のデータを QIODevice に書き込みます
bool writeCustomSettings(QIODevice &device, const QSettings::SettingsMap &map)
{
QTextStream out(&device);
out.setCodec("UTF-8"); // UTF-8エンコーディングを使用
// ヘッダーコメント
out << "# My Custom Settings File" << "\n";
out << "# Created by QSettings Custom Format Example" << "\n";
out << "\n";
// SettingsMap の内容を書き込む
QMapIterator<QString, QVariant> i(map);
while (i.hasNext()) {
i.next();
out << i.key() << "=" << i.value().toString() << "\n";
}
// 書き込みエラーをチェック
return device.error() == QIODevice::NoError;
}
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
// アプリケーション情報の設定 (QSettingsで使用される)
QCoreApplication::setOrganizationName("MyCompany");
QCoreApplication::setApplicationName("CustomSettingsApp");
// 1. カスタムフォーマットの登録
// "txtsettings" という拡張子に関連付けます
const QSettings::Format CustomTextFormat =
QSettings::registerFormat("txtsettings", readCustomSettings, writeCustomSettings);
if (CustomTextFormat == QSettings::InvalidFormat) {
qDebug() << "Failed to register custom settings format!";
return 1;
}
// 2. カスタムフォーマットを使用してQSettingsオブジェクトを作成
// ファイル名を明示的に指定する場合
QString settingsFilePath = "my_custom_settings.txtsettings";
QSettings settings(settingsFilePath, CustomTextFormat);
qDebug() << "Settings file path:" << settings.fileName();
// 3. 設定値の書き込み
qDebug() << "Writing settings...";
settings.setValue("user/name", "Taro Yamada");
settings.setValue("user/email", "[email protected]");
settings.setValue("app/version", "1.0.0");
settings.setValue("window/geometry", QRect(100, 100, 800, 600)); // QVariantはQRectも保持可能
settings.setValue("debugMode", true);
// 設定をファイルに同期 (明示的に呼び出すか、QSettingsのデストラクタが自動で行う)
settings.sync();
if (settings.status() == QSettings::NoError) {
qDebug() << "Settings written successfully.";
} else {
qDebug() << "Error writing settings:" << settings.status();
}
// 4. 設定値の読み込み
qDebug() << "\nReading settings...";
QString userName = settings.value("user/name").toString();
QString userEmail = settings.value("user/email").toString();
QString appVersion = settings.value("app/version").toString();
QRect windowGeometry = settings.value("window/geometry").toRect();
bool debugMode = settings.value("debugMode").toBool(); // 文字列"true"からbool値に変換可能
qDebug() << "User Name:" << userName;
qDebug() << "User Email:" << userEmail;
qDebug() << "App Version:" << appVersion;
qDebug() << "Window Geometry:" << windowGeometry;
qDebug() << "Debug Mode:" << debugMode;
// 存在しないキーの読み込み(デフォルト値が返される)
QString nonexistentKey = settings.value("nonexistent/key", "Default Value").toString();
qDebug() << "Non-existent Key:" << nonexistentKey;
// 5. 設定ファイルの削除 (テスト用)
// QFile::remove(settingsFilePath);
// qDebug() << "Settings file removed for next run.";
return 0;
}
CMakeLists.txt (または Qmake プロジェクトファイル)
CMakeLists.txt
:
cmake_minimum_required(VERSION 3.14)
project(CustomSettingsApp LANGUAGES CXX)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
find_package(Qt6 REQUIRED COMPONENTS Core)
add_executable(CustomSettingsApp main.cpp)
target_link_libraries(CustomSettingsApp PRIVATE Qt6::Core)
ビルドと実行
- 上記コードを
main.cpp
として保存し、CMakeLists.txt
を作成します。 - ターミナルでプロジェクトのルートディレクトリに移動し、ビルドディレクトリを作成して移動します。
mkdir build cd build
- CMake を実行してビルドファイルを生成します。
cmake ..
- ビルドを実行します。
cmake --build .
- アプリケーションを実行します。
./CustomSettingsApp
出力例
Settings file path: "my_custom_settings.txtsettings"
Writing settings...
Settings written successfully.
Reading settings...
User Name: "Taro Yamada"
User Email: "[email protected]"
App Version: "1.0.0"
Window Geometry: QRect(100, 100, 800, 600)
Debug Mode: true
Non-existent Key: "Default Value"
# My Custom Settings File
# Created by QSettings Custom Format Example
user/name=Taro Yamada
user/[email protected]
app/version=1.0.0
window/geometry=QRect(100, 100, 800, 600)
debugMode=true
-
readCustomSettings
関数:QIODevice &device
: 読み込み元のデバイス(通常はQFile
)を受け取ります。QSettings::SettingsMap &map
: 読み込んだキーと値のペアを格納するQMap<QString, QVariant>
です。QTextStream
を使ってデバイスから1行ずつ読み込みます。- 空行や
#
で始まるコメント行はスキップします。 key=value
の形式で=
の位置を特定し、キーと値を抽出してmap
に挿入します。QVariant
は多くの標準C++型やQtの型を自動的に変換できるため、ここでは文字列として読み込んだ値をそのままQVariant
に格納しています。後でsettings.value().toType()
で取り出す際に適切な型に変換されます。
-
writeCustomSettings
関数:QIODevice &device
: 書き込み先のデバイスを受け取ります。const QSettings::SettingsMap &map
: 書き込むデータを含むQMap<QString, QVariant>
です。const
なので変更できません。QTextStream
を使ってデバイスに1行ずつ書き込みます。map
の内容をイテレータで走査し、各キーと値(toString()
で文字列化)を「キー=値」の形式で書き出します。
-
main
関数:QCoreApplication
の初期化と、setOrganizationName()
、setApplicationName()
でアプリケーション情報を設定します。これはQSettings
のデフォルト動作に影響しますが、この例では明示的にファイル名を指定しているため直接的な影響は少ないです。QSettings::registerFormat()
を呼び出し、カスタムの読み書き関数を登録します。この関数は、登録されたフォーマットの識別子(QSettings::Format
型の整数値)を返します。この識別子を使ってQSettings
オブジェクトを作成します。QSettings settings(settingsFilePath, CustomTextFormat);
のように、登録したフォーマットを指定してQSettings
オブジェクトを初期化します。settings.setValue()
で設定値を書き込みます。QVariant
は多くのQt型(QRect
、QSize
、QPoint
など)を自動的に変換して保持できるため、明示的な変換は不要です。settings.sync()
を呼び出して、設定をファイルに強制的に書き込みます。通常はアプリケーション終了時に自動的に書き込まれますが、安全のために明示的に呼び出すこともあります。settings.value()
で設定値を読み込みます。取り出す際にはtoString()
やtoRect()
、toBool()
などの適切な変換関数を使用します。
しかし、多くのQtアプリケーションでは、そこまで低レベルな設定の仕組みを自作する必要はありません。Qtは、より直接的で便利な設定管理の方法をいくつか提供しています。これらの方法は、QSettings::SettingsMap
を直接扱うことなく、アプリケーションの設定を永続化できます。
代替方法は、大きく分けて以下の2つのカテゴリに分類できます。
- QSettingsの標準機能を使用する: ほとんどのケースで推奨される方法です。
- Qtが提供する他のシリアライズ機構を使用する: より複雑なデータ構造や、設定以外のデータ永続化に適しています。
QSettings の標準機能を使用する(最も一般的で推奨される方法)
QSettings
クラスは、プラットフォームネイティブな設定保存メカニズム(Windowsのレジストリ、macOSのplist、Unix/LinuxのINIファイルなど)を抽象化し、シンプルなAPIで設定を扱うことができます。通常、開発者がQSettings::SettingsMap
を意識する必要はありません。
特徴
- カスタム設定形式の登録は、特定のニーズがある場合にのみ検討すべきです。
- 異なるデータ型(
QString
,int
,bool
,QRect
,QSize
など)をQVariant
経由で透過的に扱える。 - 簡単なAPI (
setValue()
,value()
) でキーと値を操作できる。 - OSネイティブな形式で設定を保存するため、ユーザーやシステム管理者が設定を探しやすい。
コード例
#include <QCoreApplication>
#include <QSettings>
#include <QDebug>
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
// アプリケーション情報の設定 (QSettingsが設定ファイルの場所を決定する際に使用)
QCoreApplication::setOrganizationName("MyCompany");
QCoreApplication::setApplicationName("MyApp");
// QSettingsオブジェクトの作成
// デフォルトのコンストラクタを使用すると、OSネイティブな設定形式が使われる
QSettings settings; // 例: Windowsならレジストリ、Linuxなら~/.config/MyCompany/MyApp.conf (INI形式)
// 設定値の書き込み
settings.setValue("user/name", "Alice");
settings.setValue("app/version", 1.2);
settings.setValue("window/geometry", QRect(100, 100, 800, 600));
settings.setValue("flags/darkMode", true);
// 設定値の読み込み (存在しない場合はデフォルト値を指定可能)
QString userName = settings.value("user/name", "Guest").toString();
double appVersion = settings.value("app/version", 1.0).toDouble();
QRect windowGeometry = settings.value("window/geometry", QRect(0,0,640,480)).toRect();
bool darkMode = settings.value("flags/darkMode", false).toBool();
qDebug() << "User Name:" << userName;
qDebug() << "App Version:" << appVersion;
qDebug() << "Window Geometry:" << windowGeometry;
qDebug() << "Dark Mode:" << darkMode;
// 設定の削除
// settings.remove("user/name"); // 特定のキーを削除
// settings.clear(); // すべての設定を削除
// QSettingsは通常、デストラクタで自動的に保存されるが、明示的に同期することも可能
settings.sync();
return 0;
}
考察
ほとんどのアプリケーションは、このQSettings
の標準機能で十分です。QSettings::SettingsMap
を直接触る必要はありません。
Qtが提供する他のシリアライズ機構を使用する
より複雑なデータ構造を保存したい場合や、設定ファイルとは異なる目的でデータを永続化したい場合は、QSettings
以外のQtのシリアライズ機構を使用することを検討できます。
a. QDataStream と QFile を使用する
QDataStream
は、バイナリ形式でデータを読み書きするためのクラスです。QFile
と組み合わせることで、任意のバイナリファイルに複雑なデータ構造を効率的に保存できます。
特徴
Q_DECLARE_METATYPE
とqRegisterMetaTypeStreamOperators
を使用することで、カスタム型もQDataStream
で扱えるようになる。- ファイル形式は開発者が完全に制御できるため、バージョンアップ時の互換性維持は開発者の責任となる。
- 高速かつコンパクトなデータ保存が可能。
- 任意のC++オブジェクトやQtのデータ型をバイナリ形式でシリアライズできる。
コード例
#include <QCoreApplication>
#include <QFile>
#include <QDataStream>
#include <QDebug>
#include <QMap> // QSettings::SettingsMap と同様に QMap を使う例
#include <QVariant>
#include <QPoint>
// 保存するカスタムデータ型 (例として)
struct AppState {
QString lastUser;
int windowWidth;
QPoint lastPosition;
};
Q_DECLARE_METATYPE(AppState) // QVariantに格納するために必要
// QDataStream で AppState を読み書きするためのオペレーターオーバーロード
QDataStream &operator<<(QDataStream &out, const AppState &data) {
out << data.lastUser << data.windowWidth << data.lastPosition;
return out;
}
QDataStream &operator>>(QDataStream &in, AppState &data) {
in >> data.lastUser >> data.windowWidth >> data.lastPosition;
return in;
}
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
// QDataStream でカスタム型を扱うための登録 (必要に応じて)
qRegisterMetaType<AppState>("AppState");
qRegisterMetaTypeStreamOperators<AppState>();
QString fileName = "appdata.bin";
// データ書き込み
QFile outFile(fileName);
if (outFile.open(QIODevice::WriteOnly)) {
QDataStream out(&outFile);
out.setVersion(QDataStream::Qt_6_0); // バージョン設定 (互換性維持のため重要)
// QSettings::SettingsMap に似た QMap<QString, QVariant> を保存する例
QMap<QString, QVariant> settingsMap;
settingsMap["user/name"] = "Bob";
settingsMap["app/theme"] = "dark";
settingsMap["data/counter"] = 123;
out << settingsMap;
// カスタムデータ型を保存する例
AppState stateToWrite = {"developer", 1024, QPoint(50, 50)};
out << stateToWrite;
outFile.close();
qDebug() << "Data written to" << fileName;
} else {
qDebug() << "Error opening file for writing:" << outFile.errorString();
}
// データ読み込み
QFile inFile(fileName);
if (inFile.open(QIODevice::ReadOnly)) {
QDataStream in(&inFile);
in.setVersion(QDataStream::Qt_6_0); // 書き込み時と同じバージョンを設定
// QMap<QString, QVariant> の読み込み
QMap<QString, QVariant> loadedSettingsMap;
in >> loadedSettingsMap;
qDebug() << "\nLoaded settings map:" << loadedSettingsMap;
qDebug() << "User name from map:" << loadedSettingsMap.value("user/name").toString();
// カスタムデータ型の読み込み
AppState loadedState;
in >> loadedState;
qDebug() << "\nLoaded AppState:";
qDebug() << " Last User:" << loadedState.lastUser;
qDebug() << " Window Width:" << loadedState.windowWidth;
qDebug() << " Last Position:" << loadedState.lastPosition;
inFile.close();
qDebug() << "Data read from" << fileName;
} else {
qDebug() << "Error opening file for reading:" << inFile.errorString();
}
// QFile::remove(fileName); // テスト後にファイルを削除
return 0;
}
考察
QDataStream
は、アプリケーション固有のデータを独自の形式で保存する際に非常に強力です。QSettings::SettingsMap
のようにキーと値のペアを保存するだけでなく、任意の複雑なオブジェクトグラフをシリアライズできます。しかし、ファイル形式の互換性管理は開発者の責任となります。
b. JSON / XML ファイルとして保存する
QJsonDocument
, QJsonObject
, QJsonArray
(JSON) や QXmlStreamWriter
, QXmlStreamReader
(XML) を使用して、設定やデータを構造化されたテキスト形式で保存することも一般的です。
特徴
QVariant
とJSON/XMLのデータ型は比較的容易にマッピングできる。- スキーマ定義やバリデーションを行うことで、データの一貫性を保ちやすい。
- 異なるプログラミング言語やシステムとのデータ連携が容易。
- 人間が読みやすく、デバッグしやすい(JSON/XML)。
コード例 (JSON)
#include <QCoreApplication>
#include <QFile>
#include <QJsonDocument>
#include <QJsonObject>
#include <QJsonArray>
#include <QDebug>
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
QString fileName = "appsettings.json";
// 設定データを作成 (QJsonObjectでキーと値を表現)
QJsonObject settingsObject;
settingsObject["userName"] = "Charlie";
settingsObject["appVersion"] = 2.0;
settingsObject["darkMode"] = true;
// より複雑なデータ (配列)
QJsonArray recentFilesArray;
recentFilesArray.append("/home/user/doc1.txt");
recentFilesArray.append("/home/user/project/main.cpp");
settingsObject["recentFiles"] = recentFilesArray;
// QJsonDocument に格納
QJsonDocument doc(settingsObject);
// JSONをファイルに書き込み
QFile outFile(fileName);
if (outFile.open(QIODevice::WriteOnly | QIODevice::Text)) {
outFile.write(doc.toJson(QJsonDocument::Indented)); // 整形して書き込む
outFile.close();
qDebug() << "JSON settings written to" << fileName;
} else {
qDebug() << "Error opening JSON file for writing:" << outFile.errorString();
}
// JSONをファイルから読み込み
QFile inFile(fileName);
if (inFile.open(QIODevice::ReadOnly | QIODevice::Text)) {
QByteArray jsonData = inFile.readAll();
inFile.close();
QJsonDocument loadedDoc = QJsonDocument::fromJson(jsonData);
if (!loadedDoc.isNull() && loadedDoc.isObject()) {
QJsonObject loadedSettings = loadedDoc.object();
QString userName = loadedSettings["userName"].toString();
double appVersion = loadedSettings["appVersion"].toDouble();
bool darkMode = loadedSettings["darkMode"].toBool();
qDebug() << "\nLoaded JSON Settings:";
qDebug() << " User Name:" << userName;
qDebug() << " App Version:" << appVersion;
qDebug() << " Dark Mode:" << darkMode;
if (loadedSettings.contains("recentFiles") && loadedSettings["recentFiles"].isArray()) {
QJsonArray loadedRecentFiles = loadedSettings["recentFiles"].toArray();
qDebug() << " Recent Files:";
for (const QJsonValue &value : loadedRecentFiles) {
qDebug() << " -" << value.toString();
}
}
} else {
qDebug() << "Error: Invalid JSON document or not an object.";
}
} else {
qDebug() << "Error opening JSON file for reading:" << inFile.errorString();
}
// QFile::remove(fileName); // テスト後にファイルを削除
return 0;
}
考察
JSONやXMLは、複雑な構造を持つ設定や、アプリケーション間で共有されるべきデータに適しています。QSettings::SettingsMap
のように単純なキーと値のペアだけでなく、ネストされたオブジェクトや配列も自然に表現できます。
- より複雑なデータ構造や、設定以外の目的でデータを永続化したい場合は、
QDataStream
(バイナリ)やQJsonDocument
/QXmlStreamWriter
/Reader
(テキストベース)などの代替手段を検討すると良いでしょう。これらはQSettings::SettingsMap
が提供する単純なキーバリューペア以上の柔軟性を提供します。 QSettings::SettingsMap
は、QSettings
が提供する組み込みフォーマット(レジストリ、INI、plistなど)では対応できない、独自のファイル形式で設定を保存したい場合に、そのカスタム形式の読み書き関数を実装する際に必要となります。- ほとんどのQtアプリケーションの設定管理には、
QSettings
の標準機能を使用するのが最も簡単で推奨されます。QSettings::SettingsMap
を直接扱う必要はありません。