Qtフレームワークでの設定情報のシリアライズと復元

アプリケーション状態の永続化

一般的なデスクトップアプリケーションでは、ユーザーが最終的に終了した時の状態(ウィンドウのサイズ、選択されたフォント、ツールバーの表示状態など)を、次回起動時に復元することが求められます。これを実現するには、アプリケーション終了時に状態をファイルやデータベースに保存し、起動時にそれらを読み出す処理が必要です。

設定データの保存形式には、XMLやJSONといったテキスト形式、SQLiteなどの軽量データベース、あるいは独自のバイナリ形式などがあります。Qtフレームワークを利用した開発では、QDataStreamクラスを使用して、設定情報をバイナリデータとして直接ファイルに書き出すアプローチが有効です。この方法は、データの読み書きが効率的であることに加え、バイナリ形式であるためユーザーによる意図しない改変や悪意ある書き換えからデータを保護しやすいという利点があります。

設定管理クラスの設計

設定の保存と読み込みを担当するクラスを設計します。このクラスでは、引数を持たないコンストラクタでファイルから設定を読み込み(デシリアライズ)、設定値を引数に持つコンストラクタでメモリ上のデータを保持し、保存メソッドでファイルへ書き出す(シリアライズ)役割を担います。以下にクラス定義の例を示します。

#ifndef SETTINGS_H
#define SETTINGS_H

#include <QObject>
#include <QFont>
#include <QDataStream>

class AppSettings : public QObject
{
    Q_OBJECT

    struct UiState {
        QFont editorFont;
        bool isToolBarVisible;
        bool isStatusBarVisible;
        bool isLineWrapEnabled;
    };

    UiState m_state;
    bool m_isLoadSuccess;

    bool readFromFile();
    bool writeToFile();

public:
    explicit AppSettings(QObject* parent = nullptr);
    explicit AppSettings(QFont font, bool toolBar, bool statusBar, bool wrap, QObject* parent = nullptr);

    QFont getEditorFont() const;
    bool isToolBarVisible() const;
    bool isStatusBarVisible() const;
    bool isLineWrapEnabled() const;
    bool isSettingsValid() const;
    bool save();
};

#endif // SETTINGS_H

実装詳細

実装ファイルでは、QDataStreamを用いてデータのシリアライズを行います。データの互換性を保つため、ストリームのバージョンを明示的に設定することが重要です。

#include "Settings.h"
#include <QFile>
#include <QApplication>
#include <QIODevice>

// 設定ファイルから読み込むコンストラクタ
AppSettings::AppSettings(QObject* parent) : QObject(parent)
{
    m_isLoadSuccess = readFromFile();
}

// 現在の状態を保持して保存用に使用するコンストラクタ
AppSettings::AppSettings(QFont font, bool toolBar, bool statusBar, bool wrap, QObject* parent)
    : QObject(parent)
{
    m_state.editorFont = font;
    m_state.isToolBarVisible = toolBar;
    m_state.isStatusBarVisible = statusBar;
    m_state.isLineWrapEnabled = wrap;
    m_isLoadSuccess = true;
}

bool AppSettings::readFromFile()
{
    bool result = false;
    QString path = QApplication::applicationDirPath() + "/user_state.dat";
    QFile configFile(path);

    if (configFile.open(QIODevice::ReadOnly)) {
        QDataStream in(&configFile);
        in.setVersion(QDataStream::Qt_5_15);

        in >> m_state.editorFont;
        in >> m_state.isToolBarVisible;
        in >> m_state.isStatusBarVisible;
        in >> m_state.isLineWrapEnabled;

        configFile.close();
        result = true;
    }

    return result;
}

bool AppSettings::writeToFile()
{
    bool result = false;
    QString path = QApplication::applicationDirPath() + "/user_state.dat";
    QFile configFile(path);

    if (configFile.open(QIODevice::WriteOnly)) {
        QDataStream out(&configFile);
        out.setVersion(QDataStream::Qt_5_15);

        out << m_state.editorFont;
        out << m_state.isToolBarVisible;
        out << m_state.isStatusBarVisible;
        out << m_state.isLineWrapEnabled;

        configFile.close();
        result = true;
    }

    return result;
}

// Getterメソッドの実装
QFont AppSettings::getEditorFont() const { return m_state.editorFont; }
bool AppSettings::isToolBarVisible() const { return m_state.isToolBarVisible; }
bool AppSettings::isStatusBarVisible() const { return m_state.isStatusBarVisible; }
bool AppSettings::isLineWrapEnabled() const { return m_state.isLineWrapEnabled; }
bool AppSettings::isSettingsValid() const { return m_isLoadSuccess; }
bool AppSettings::save() { return writeToFile(); }

メインウィンドウへの統合

実際のアプリケーションでは、ウィンドウの初期化時に設定を読み込み、終了時に設定を書き込みます。以下の例では、MainWindowの初期化処理(二段階初期化の一部)とデストラクタで設定クラスを活用しています。

設定の適用(起動時)

bool MainWindow::initializeUI()
{
    bool ret = true;
    
    // デフォルトの初期化処理...
    ret = ret && initMenuBar();
    ret = ret && initToolBar();
    ret = ret && initStatusBar();
    ret = ret && initEditor();

    AppSettings currentSettings;

    // 設定ファイルの読み込みが成功した場合のみ状態を復元
    if (currentSettings.isSettingsValid()) {
        textEditor->setFont(currentSettings.getEditorFont());

        // 自動折り返しの設定復元
        if (!currentSettings.isLineWrapEnabled()) {
            textEditor->setLineWrapMode(QPlainTextEdit::NoWrap);
            updateMenuAction("word_wrap", false);
        }

        // ツールバーの表示状態復元
        if (!currentSettings.isToolBarVisible()) {
            mainToolBar->setVisible(false);
            updateMenuAction("show_toolbar", false);
        }

        // ステータスバーの表示状態復元
        if (!currentSettings.isStatusBarVisible()) {
            mainStatusBar->setVisible(false);
            updateMenuAction("show_statusbar", false);
        }
    }

    return ret;
}

設定の保存(終了時)

MainWindow::~MainWindow()
{
    // 現在のUI状態を取得
    QFont currentFont = textEditor->font();
    bool wrapEnabled = (textEditor->lineWrapMode() == QPlainTextEdit::WidgetWidth);
    bool tbVisible = isMenuActionChecked("show_toolbar");
    bool sbVisible = isMenuActionChecked("show_statusbar");

    // 設定オブジェクトを作成してファイルへ保存
    AppSettings snapshot(currentFont, tbVisible, sbVisible, wrapEnabled);
    snapshot.save();
}

この実装により、アプリケーションは終了時の状態を正確に記憶し、次回の起動時にはユーザーが期待する環境を即座に再現できるようになります。バイナリストリームを採用することで、データの読み書きオーバーヘッドが小さく、かつ設定ファイルの直接的な編集を防ぐことができるため、アプリケーションの安定性とセキュリティが向上します。

タグ: Qt C++ QDataStream configuration Serialization

5月27日 16:04 投稿