Qtの基礎 - INIファイル

提供:MochiuWiki : SUSE, EC, PCB
ナビゲーションに移動 検索に移動

概要

INIファイルは、シンプルな設定ファイル形式であり、多くのアプリケーションで使用されている。
Qtでは、QSettingsクラスを使用してINIファイルを読み書きすることができる。

QSettingsクラスは、プラットフォームに依存しない方法で設定を保存・読み込みする機能を提供する。
INIファイル形式は、クロスプラットフォームで使用可能であり、人間が読みやすい形式である。

INIファイルの基本構造は、セクションとキー・値のペアで構成されている。
セクションは角括弧[]で囲まれ、その下にキー・値のペアが続く。

 [General]
 LastUser=JohnDoe
 AutoSave=true
 
 [Window]
 Width=800
 Height=600


QtでINIファイルを使用する場合の主なメリットは、使いやすさと柔軟性である。
QSettingsクラスを使用することにより、設定の読み書きが簡単に行うことができる。
また、アプリケーションの設定を外部ファイルで管理できるため、設定の変更や共有が容易になる。

INIファイルの操作には、値の読み取り、書き込み、削除等の基本的な操作がある。
QSettingsクラスは、これらの操作を簡単に行うためのメソッドを提供している。

ただし、INIファイルにはいくつかの制限もある。
例えば、複雑なデータ構造の保存には適していない。
また、大量のデータを扱う場合は、パフォーマンスの面で他の形式 (例: データベース) の方が適している場合がある。

INIファイルを使用する時は、セキュリティにも注意が必要である。
特に、設定ファイルに機密情報を保存する場合は、適切な暗号化や権限設定を行うことが重要である。


INIファイルの書き込み

以下の例では、ソフトウェアにおけるウインドウの状態を保存している。

ウインドウの座標、縦横のサイズ、状態、最少幅、タイトル文字列をSetting.iniファイルに保存する。
もし、Settings.iniファイルが存在する場合は上書きする。

 void MainWindow::saveSetting()
 {
    QSettings settings("settings.ini", QSettings::IniFormat, this);
    settings.beginGroup("MainWindow");
    settings.setValue("pos", pos());
 
    if(!isMaximized())
    {
       settings.setValue("size", size());
    }
 
    settings.setValue("maximized", isMaximized());
    settings.setValue("miniwid", minimumWidth());
    settings.setValue("title", windowTitle());
 
    settings.endGroup();
 }


以下の例では、ソフトウェア終了時に、ウインドウの幅と高さをINIファイルに保存している。

 // mainwindow.cpp
 
 #include <QMessageBox>
 #include <QSettings>
 #include <QSize>
 #include "mainwindow.h"
 #include "ui_mainwindow.h"
 
 MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWindow)
 {
    ui->setupUi(this);
 
    ORG_NAME = "My Company";
    APP_NAME = "My Tool";
 
    QSettings settings(ORG_NAME, APP_NAME);
    int width = settings.value("Window / Width").toInt();
    int height = settings.value("Window / Height").toInt();
 
    this->resize(QSize(width, height));
 }
 
 MainWindow::~MainWindow()
 {
    delete ui;
 }
 
 // ソフトウェアの終了処理はcloseEventをオーバーライドすることで実現できる
 void MainWindow::closeEvent(QCloseEvent *event)
 {
    int width = this->geometry().width();
    int height = this->geometry().height();
 
    QMessageBox::information(this, "Finish Timing", tr("window width : %1 \nwindow height : %2").arg(width).arg(height));
 
    QSettings settings(ORG_NAME, APP_NAME);
 
    settings.setValue("Window / Width", width);
    settings.setValue("Window / Height", height);
 }


 // mainwindow.h
 
 #ifndef MAINWINDOW_H
 #define MAINWINDOW_H
 
 #include <QMainWindow>
 
 namespace Ui {class MainWindow;}
 
 class MainWindow : public QMainWindow
 {
    Q_OBJECT
 
 private:
     const QString ORG_NAME;
     const QString APP_NAME;
 
 public:
    explicit MainWindow(QWidget *parent = 0);
    ~MainWindow();
 
 private:
    Ui::MainWindow *ui;
 
 protected:
    virtual void closeEvent(QCloseEvent *event);
 };
 
 #endif // MAINWINDOW_H



INIファイルの読み込み

以下の例では、前回のウインドウの状態を復元している。
Setting.iniファイルから必要な情報を読み込み、画面の状態を変更する。

Settings.iniファイルが存在しない場合またはpos等のキー値が無ければ何もしない。

 bool MainWindow::loadSetting()
 {
    QSettings settings("settings.ini", QSettings::IniFormat, this);
    settings.beginGroup("MainWindow");
 
    if(settings.contains("pos"))
    {
       move(settings.value("pos", QPoint(50, 40)).toPoint());
    }
 
    if(settings.contains("size"))
    {
       resize(settings.value("size", QSize(800, 720)).toSize());
    }
 
    if(settings.value("maximized").toBool())
    {
       setWindowState(windowState() | Qt::WindowMaximized);
    }
 
    if(settings.contains("miniwid"))
    {
       setMinimumWidth(settings.value("miniwid", 400).toInt());
    }
 
    if(settings.contains("title"))
    {
       setWindowTitle(settings.value("title", "なし").toString());
    }
 
    settings.endGroup();
 }


以下に、設定ファイル(Settings.ini)の内容を示す。
左上位置(46, 15)、画面サイズ(800x600)、最大化(false)、最少幅(400)、画面のタイトルが"サンプル画面"の場合である。

 [MainWindow]
 pos=@Point(46 15)
 size=@Size(800 600)
 maximized=false
 miniwid=400
 title=\x30b5\x30f3\x30d7\x30eb\x753b\x9762



複数の設定ファイルを扱う

以下の例では、異なる目的や範囲のための複数の設定ファイルを管理している。
グローバル設定、ユーザ固有の設定、プロジェクト固有の設定を別々のファイルで扱っている。

また、各設定ファイルの書き込み権限を確認して、エラーが発生した場合にはログを出力する。

 #include <QSettings>
 #include <QFileInfo>
 #include <QDebug>
 
 // QSettingsオブジェクトのステータスを確認
 void handleSettingsError(const QSettings &settings, const QString &operation)
 {
    if (settings.status() != QSettings::NoError) {
       qCritical() << "Error occurred during" << operation << ": " << settings.status();
    }
 }

 int main()
 {
    QSettings globalSettings("MyCompany", "MyApp");
    globalSettings.setValue("language", "Japanese");
    handleSettingsError(globalSettings, "global settings");
 
    QSettings userSettings("MyCompany", "MyApp", QSettings::IniFormat, QSettings::UserScope);
    userSettings.setValue("theme", "dark");
    handleSettingsError(userSettings, "user settings");
 
    QString projectConfigPath = "./project_config.ini";
    QFileInfo fileInfo(projectConfigPath);
    if (!fileInfo.isWritable()) {
       qWarning() << "Project config file is not writable:" << projectConfigPath;
    }
    else {
       QSettings projectSettings(projectConfigPath, QSettings::IniFormat);
       projectSettings.setValue("projectName", "MyProject");
       handleSettingsError(projectSettings, "project settings");
    }
 
    qDebug() << "Global language:" << globalSettings.value("language", "Default").toString();
    qDebug() << "User theme:" << userSettings.value("theme", "Default").toString();
 
    return 0;
 }



階層的な設定構造を使用する

以下の例では、設定をツリー構造で整理して、/を使用してキーを階層化している。
ウインドウのサイズや位置、ユーザ情報等をを階層的に保存している。

グループを使用して関連する設定にアクセスする手順も示している。

 #include <QSettings>
 #include <QDebug>
 
 // QSettingsオブジェクトのステータスを確認
 void handleSettingsError(const QSettings &settings, const QString &operation)
 {
    if (settings.status() != QSettings::NoError) {
       qCritical() << "Error occurred during" << operation << ": " << settings.status();
    }
 }
 
 int main()
 {
    QSettings settings("MyCompany", "MyApp");
 
    settings.setValue("window/size/width", 800);
    settings.setValue("window/size/height", 600);
    settings.setValue("window/position/x", 100);
    settings.setValue("window/position/y", 100);
    settings.setValue("user/name", "John Doe");
    settings.setValue("user/email", "john@example.com");
    handleSettingsError(settings, "setting values");
 
    // QVariantオブジェクトの有効性を確認
    QVariant widthValue = settings.value("window/size/width");
    if (!widthValue.isValid()) {
       qWarning() << "Failed to read window width";
    }
    else {
       qDebug() << "Window width:" << widthValue.toInt();
    }
 
    // QVariantオブジェクトの有効性を確認
    QVariant nameValue = settings.value("user/name");
    if (!nameValue.isValid()) {
       qWarning() << "Failed to read user name";
    }
    else {
       qDebug() << "User name:" << nameValue.toString();
    }
 
    settings.beginGroup("window");
    if (settings.group() != "window") {
       qWarning() << "Failed to begin group 'window'";
    }
    else {
       qDebug() << "Window size:" << settings.value("size/width").toInt() << "x" 
                << settings.value("size/height").toInt();
    }
    settings.endGroup();
 
    handleSettingsError(settings, "reading values");
 
    return 0;
 }



一時的な設定を管理する

以下の例では、メモリ内の設定を使用して、永続的なファイルを作成せずに一時的な設定を扱っている。
これは、QSettings::setDefaultFormatメソッドおよびQSettings::setPathメソッドを使用してメモリ内設定を構成している。

 #include <QSettings>
 #include <QDebug>
 
 // QSettingsオブジェクトのステータスを確認
 void handleSettingsError(const QSettings &settings, const QString &operation)
 {
    if (settings.status() != QSettings::NoError) {
       qCritical() << "Error occurred during" << operation << ": " << settings.status();
    }
 }
 
 int main()
 {
    if (!QSettings::setDefaultFormat(QSettings::IniFormat)) {
       qCritical() << "Failed to set default format to INI";
       return -1;
    }
 
    if (!QSettings::setPath(QSettings::IniFormat, QSettings::UserScope, QSettings::defaultFormat())) {
       qCritical() << "Failed to set path for temporary settings";
       return -1;
    }
 
    QSettings settings;
    settings.setValue("tempSetting", "This is a temporary setting");
    handleSettingsError(settings, "setting temporary value");
 
    // QVariantオブジェクトの有効性を確認
    QVariant tempValue = settings.value("tempSetting");
    if (!tempValue.isValid()) {
       qWarning() << "Failed to read temporary setting";
    }
    else {
       qDebug() << "Temp setting:" << tempValue.toString();
    }
 
    handleSettingsError(settings, "reading temporary value");
 
    return 0;
 }



設定のインポートとエクスポート

以下の例では、設定をファイルにエクスポートして、そのファイルから設定をインポートする機能を提供している。

エクスポート時には全ての設定をテキストファイルに書き出し、インポート時にはファイルを読み込んで設定を復元している。

 #include <QSettings>
 #include <QFile>
 #include <QTextStream>
 #include <QDebug>
 
 // QSettingsオブジェクトのステータスを確認
 void handleSettingsError(const QSettings &settings, const QString &operation)
 {
    if (settings.status() != QSettings::NoError) {
       qCritical() << "Error occurred during" << operation << ": " << settings.status();
    }
 }
 
 bool exportSettings(const QString &filename)
 {
    QSettings settings("MyCompany", "MyApp");
    QFile file(filename);
    if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) {
       qCritical() << "Failed to open file for writing:" << filename;
       return false;
    }
 
    QTextStream out(&file);
    for (const QString& key : settings.allKeys()) {
       out << key << "=" << settings.value(key).toString() << "\n";
    }
    file.close();
    qDebug() << "Settings exported to" << filename;
 
    return true;
 }
 
 bool importSettings(const QString &filename)
 {
    QSettings settings("MyCompany", "MyApp");
    QFile file(filename);
    if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
       qCritical() << "Failed to open file for reading:" << filename;
       return false;
    }
 
    QTextStream in(&file);
    while (!in.atEnd()) {
       QString line = in.readLine();
       QStringList parts = line.split('=');
       if (parts.size() == 2) {
          settings.setValue(parts[0].trimmed(), parts[1].trimmed());
       }
       else {
          qWarning() << "Invalid line in settings file:" << line;
       }
    }
    file.close();
 
    qDebug() << "Settings imported from" << filename;
 
    handleSettingsError(settings, "importing settings");
 
    return true;
 }
 
 int main()
 {
    QSettings settings("MyCompany", "MyApp");
    settings.setValue("setting1", "value1");
    settings.setValue("setting2", "value2");
    handleSettingsError(settings, "setting initial values");
 
    if (!exportSettings("settings_export.txt")) {
       qCritical() << "Failed to export settings";
       return -1;
    }
 
    settings.clear();
 
    if (!importSettings("settings_export.txt")) {
       qCritical() << "Failed to import settings";
       return -1;
    }
 
    QVariant setting1 = settings.value("setting1");
    QVariant setting2 = settings.value("setting2");
 
    // QVariantオブジェクトの有効性を確認
    if (!setting1.isValid() || !setting2.isValid()) {
       qWarning() << "Failed to read imported settings";
    }
    else {
       qDebug() << "Imported setting1:" << setting1.toString();
       qDebug() << "Imported setting2:" << setting2.toString();
    }
 
    handleSettingsError(settings, "reading imported settings");
 
    return 0;
 }



クロスプラットフォームで一貫した設定管理を行う

以下の例では、Windowsではレジストリ、その他のプラットフォームではINIファイルを使用する等、OSに応じて適切な設定ストレージを選択している。
同時に、プラットフォーム固有の設定とINIファイルベースの設定の両方を扱えるようにしている。

 #include <QSettings>
 #include <QDebug>
 
 // QSettingsオブジェクトのステータスを確認
 void handleSettingsError(const QSettings &settings, const QString &operation)
 {
    if (settings.status() != QSettings::NoError) {
       qCritical() << "Error occurred during" << operation << ": " << settings.status();
    }
 }
 
 int main()
 {
    QSettings nativeSettings("MyCompany", "MyApp");
    nativeSettings.setValue("nativeSetting", "This is a native setting");
    handleSettingsError(nativeSettings, "setting native value");
 
    QSettings iniSettings("settings.ini", QSettings::IniFormat);
    iniSettings.setValue("iniSetting", "This is an INI setting");
    handleSettingsError(iniSettings, "setting INI value");
 
    QSettings* settings;
 #ifdef Q_OS_WIN
    settings = &nativeSettings;  // Windowsではレジストリを使用
 #else
    settings = &iniSettings;     // その他のプラットフォームではINIファイルを使用
 #endif
 
    settings->setValue("platformSetting", "This is a platform-specific setting");
    handleSettingsError(*settings, "setting platform-specific value");
 
    QVariant nativeValue = nativeSettings.value("nativeSetting");
    QVariant iniValue = iniSettings.value("iniSetting");
    QVariant platformValue = settings->value("platformSetting");
 
    // QVariantオブジェクトの有効性を確認
    if (!nativeValue.isValid()) {
       qWarning() << "Failed to read native setting";
    }
    else {
       qDebug() << "Native setting:" << nativeValue.toString();
    }
 
    if (!iniValue.isValid()) {
       qWarning() << "Failed to read INI setting";
    }
    else {
       qDebug() << "INI setting:" << iniValue.toString();
    }
 
    if (!platformValue.isValid()) {
       qWarning() << "Failed to read platform-specific setting";
    }
    else {
       qDebug() << "Platform-specific setting:" << platformValue.toString();
    }
 
    handleSettingsError(*settings, "reading values");
 
    return 0;
 }