QFont::fromString() の落とし穴:よくあるエラーと解決策

2025-06-06

QtプログラミングにおけるQFont::fromString()は、文字列からQFontオブジェクトを生成するために使用される関数です。

具体的には、QFontオブジェクトが持つフォントファミリー、サイズ、太さ、イタリック、下線などの様々な属性を、1つの文字列として表現したものを解析し、対応するQFontオブジェクトを再構築します。

どのような時に使われるのか?

最も一般的な用途は以下の通りです。

  • フォント情報の共有
    異なるアプリケーション間や、同じアプリケーション内の異なるモジュール間でフォント情報を文字列形式でやり取りする場合に利用できます。

  • フォント設定の保存と復元
    ユーザーがアプリケーション内で選択したフォント設定(フォントの種類、サイズ、スタイルなど)をファイルや設定(QSettingsなど)に保存し、後でその設定を読み込んで同じフォントをアプリケーションに適用したい場合に非常に便利です。 QFont::toString()を使ってQFontオブジェクトを文字列に変換し、それを保存します。そして、アプリケーションの起動時などにQFont::fromString()を使ってその文字列からQFontオブジェクトを復元します。

使用例

#include <QApplication>
#include <QLabel>
#include <QFont>
#include <QString>

int main(int argc, char *argv[])
{
    QApplication app(argc, argv);

    // 元のフォントオブジェクトを作成
    QFont originalFont("Arial", 12, QFont::Bold, true); // Arial, 12pt, 太字, イタリック

    // QFontオブジェクトを文字列に変換
    QString fontString = originalFont.toString();
    qDebug() << "フォント文字列:" << fontString;

    // 文字列から新しいQFontオブジェクトを作成
    QFont restoredFont;
    if (restoredFont.fromString(fontString)) {
        qDebug() << "フォントが正常に復元されました。";
        qDebug() << "復元されたフォントファミリー:" << restoredFont.family();
        qDebug() << "復元されたフォントサイズ:" << restoredFont.pointSize();
        qDebug() << "復元されたフォントの太さ:" << restoredFont.weight();
        qDebug() << "復元されたフォントがイタリックか:" << restoredFont.italic();
    } else {
        qDebug() << "フォントの復元に失敗しました。";
    }

    // 復元されたフォントをQLabelに適用して表示
    QLabel label("Hello, Qt with restored font!");
    label.setFont(restoredFont);
    label.show();

    return app.exec();
}

QFont::toString()が生成する文字列のフォーマットについて:

QFont::toString()が生成する文字列の具体的なフォーマットはQtの内部実装に依存しますが、通常はカンマ区切りでフォントの各種属性(フォントファミリー、ポイントサイズ、ピクセルサイズ、スタイルヒント、ウェイト、スタイル、下線、取り消し線、固定幅など)が順番に並んだ形式になります。このフォーマットは、Qtのバージョンアップによって変更される可能性もありますが、fromString()はその変更に対応するように設計されています。

注意点:

  • QFont::Tag::fromString()という別の静的関数も存在しますが、これはOpenTypeフォントの機能タグ(例えば"frac"で分数など)を文字列から生成するものであり、QFont::fromString()とは用途が異なりますので注意してください。
  • フォントを復元する際には、システムにそのフォントがインストールされている必要があります。インストールされていない場合、Qtは最も近い代替フォントを選択します。
  • QFont::fromString()は、文字列が有効なフォントの記述でなければfalseを返し、フォントの復元に失敗します。


QFont::fromString: Invalid description 警告

これは最も頻繁に遭遇するエラーメッセージです。コンソールに表示される警告で、QFont::fromString()が引数として受け取った文字列を有効なフォント記述として解析できなかったことを示します。

一般的な原因とトラブルシューティング

  • Qtのバージョン間の互換性の問題

    • 原因
      QFont::toString()が生成する文字列のフォーマットは、Qtのバージョン間で厳密には同じではないことがあります。例えば、Qt4で保存したフォント文字列をQt5でfromString()しようとすると問題が発生する可能性があります。
    • トラブルシューティング
      • 可能であれば、フォント設定を保存したQtのバージョンと、それを読み込むQtのバージョンを一致させてください。
      • 古いQtバージョンで保存されたフォント設定を新しいQtバージョンで読み込む必要がある場合は、互換性レイヤーを実装するか、ユーザーにフォントを再設定してもらうなどの対策を検討する必要があります。ただし、一般的なユースケースでは、新しいQtバージョンは古いフォーマットもある程度解釈できるように設計されています。
    • 原因
      fromString()に空の文字列を渡すと、この警告が出ます。これは、ファイルからフォント設定を読み込む際に、ファイルが存在しない、または内容が空の場合によく発生します。
    • トラブルシューティング
      • fromString()を呼び出す前に、入力文字列が空でないことを確認してください。
      • 例えば、QString::isEmpty()でチェックします。
      •   QString fontDescription = readFontSettingFromFile(); // ファイルから読み込む
          QFont myFont;
          if (!fontDescription.isEmpty() && myFont.fromString(fontDescription)) {
              // 正常に復元された
          } else {
              // デフォルトフォントを設定するか、エラー処理
              myFont = QApplication::font(); // アプリケーションのデフォルトフォントを使用
              qWarning() << "フォント設定の読み込みに失敗しました。デフォルトフォントを使用します。";
          }
        

フォントは復元されたように見えるが、表示が異なる

fromString()trueを返し、フォントオブジェクトが正しく復元されたように見えても、実際にUIに適用されると期待通りの表示にならないことがあります。

一般的な原因とトラブルシューティング

  • QApplicationのインスタンスがない

    • 原因
      QFontオブジェクトを適切に機能させるには、QApplicationまたはQGuiApplicationのインスタンスが作成されている必要があります。これがない状態でQFontを操作すると、予期しない動作やエラーが発生する可能性があります。
    • トラブルシューティング
      • main関数の一番最初にQApplication a(argc, argv);のようにインスタンスを作成していることを確認してください。
  • QFontの属性が完全に一致しない

    • 原因
      QFont::fromString()が復元する属性はQFont::toString()がシリアライズしたものです。しかし、システムに存在するフォントが、元のフォントの太さやスタイル(例:Normal, Bold, Italicなど)を完全にサポートしていない場合、Qtは最も近い代替属性を選択することがあります。
    • トラブルシューティング
      • QFontInfoを使用して、実際に適用されたフォントの太さやスタイルを確認します。
      • 可能な限り、一般的なフォント(Arial, Times New Romanなど)を使用するか、アプリケーションに特定のフォントをバンドルすることを検討します。
  • システムにフォントがインストールされていない

    • 原因
      QFont::fromString()は文字列を解析してフォントの属性を復元しますが、そのフォントファミリーがユーザーのシステムにインストールされていない場合、Qtは最も近い代替フォント(通常はSans Serifなど)を使用します。
    • トラブルシューティング
      • 期待するフォントがターゲットシステムにインストールされていることを確認してください。
      • QFontInfoクラスを使用して、実際に使用されているフォントの情報を取得し、確認できます。
        QFont myFont;
        myFont.fromString(fontString); // myFontに復元を試みる
        
        QFontInfo fontInfo(myFont);
        qDebug() << "実際に使用されるフォントファミリー:" << fontInfo.family();
        qDebug() << "フォントが存在するか:" << fontInfo.exactMatch(); // exactMatch()がtrueなら指定通り
        
      • ユーザーが任意のフォントを設定できるようにしている場合、フォント選択ダイアログ (QFontDialog) を使用して、インストールされているフォントのみを選択できるようにすることが推奨されます。
  • 異なるOSでテストする
    • フォントのインストール状況やレンダリングはOSによって異なるため、可能であれば複数のOSでテストし、OS固有の問題でないかを確認します。
  • シンプルなフォントでテストする
    • 複雑なフォント設定ではなく、「Arial,10,Normal」のような非常にシンプルなフォント設定でtoString()fromString()のペアが正しく機能するかどうかをテストします。これにより、問題が複雑な属性にあるのか、基本的なメカニズムにあるのかを切り分けられます。
  • QFontInfoを使用する
    • 前述のように、QFontInfoは実際に使用されているフォントの情報を取得するための強力なツールです。exactMatch()family()などを利用して、問題の原因を特定します。
  • qDebug() を活用する
    • QFont::toString()で生成される文字列をデバッグ出力で確認し、意図しない文字やフォーマットの誤りがないかを検証します。
    • QFont::fromString()の戻り値(bool)を確認し、成功したか失敗したかをログに出力します。
    • 復元されたQFontオブジェクトのfamily(), pointSize(), weight(), italic()などの各属性を個別にqDebug()で出力し、期待通りの値になっているか確認します。


例1: フォント設定の保存と復元 (基本的な使用法)

この例は、QFont::toString() でフォント設定を文字列に変換し、それを QFont::fromString() で復元する最も基本的なシナリオです。

#include <QApplication>
#include <QLabel>
#include <QFont>
#include <QString>
#include <QDebug> // デバッグ出力用

int main(int argc, char *argv[])
{
    QApplication app(argc, argv);

    // 1. 元のフォントオブジェクトを作成
    // 例えば、アプリケーションのデフォルトフォントを使用
    QFont originalFont = app.font();
    originalFont.setFamily("Times New Roman"); // フォントファミリーを設定
    originalFont.setPointSize(14);           // ポイントサイズを設定
    originalFont.setBold(true);              // 太字に設定
    originalFont.setItalic(true);            // イタリックに設定
    originalFont.setUnderline(true);         // 下線に設定

    qDebug() << "元のフォント情報:";
    qDebug() << "  ファミリー:" << originalFont.family();
    qDebug() << "  サイズ:" << originalFont.pointSize();
    qDebug() << "  太さ:" << originalFont.weight();
    qDebug() << "  イタリック:" << originalFont.italic();
    qDebug() << "  下線:" << originalFont.underline();

    // 2. QFontオブジェクトを文字列に変換 (保存のため)
    QString fontString = originalFont.toString();
    qDebug() << "\nQFont::toString() で生成された文字列:\n " << fontString;

    // 3. 文字列から新しいQFontオブジェクトを作成 (復元のため)
    QFont restoredFont;
    // fromString() は成功した場合 true を返します
    bool success = restoredFont.fromString(fontString);

    if (success) {
        qDebug() << "\n--- フォントが正常に復元されました ---";
        qDebug() << "復元されたフォント情報:";
        qDebug() << "  ファミリー:" << restoredFont.family();
        qDebug() << "  サイズ:" << restoredFont.pointSize();
        qDebug() << "  太さ:" << restoredFont.weight();
        qDebug() << "  イタリック:" << restoredFont.italic();
        qDebug() << "  下線:" << restoredFont.underline();
    } else {
        qDebug() << "\n--- フォントの復元に失敗しました ---";
        // 失敗した場合の処理 (例: デフォルトフォントを使用する)
        restoredFont = app.font();
        qDebug() << "デフォルトフォントを使用します。";
    }

    // 4. 復元されたフォントをQLabelに適用して表示
    QLabel *label = new QLabel("Hello, Qt! This font was restored from a string.");
    label->setFont(restoredFont);
    label->setAlignment(Qt::AlignCenter);
    label->resize(400, 100);
    label->show();

    return app.exec();
}

解説

  • fromString() の戻り値をチェックすることで、復元が成功したかどうかを確認できます。
  • restoredFont.fromString(fontString) で、この文字列を解析して新しい QFont オブジェクトが構築されます。
  • originalFont.toString() で現在の QFont オブジェクトのすべての属性が1つの文字列にシリアライズされます。

例2: ユーザー設定と QSettings の使用

実際のアプリケーションでは、フォント設定を QSettings (設定ファイルやレジストリ) に保存し、次回起動時に読み込むことがよくあります。

#include <QApplication>
#include <QLabel>
#include <QFont>
#include <QString>
#include <QDebug>
#include <QSettings> // 設定保存用
#include <QPushButton> // フォント選択用
#include <QFontDialog> // フォント選択ダイアログ

// グローバルにQLabelとQSettingsを宣言して、スロットからアクセスできるようにする
QLabel *g_label = nullptr;
QSettings *g_settings = nullptr;

// フォント選択ボタンのスロット
void onChooseFontButtonClicked() {
    bool ok;
    // 現在のフォントを初期値としてフォント選択ダイアログを表示
    QFont font = QFontDialog::getFont(&ok, g_label->font(), g_label, "フォントを選択");
    if (ok) {
        // 新しいフォントをQLabelに適用
        g_label->setFont(font);

        // フォント設定をQSettingsに保存
        if (g_settings) {
            g_settings->setValue("appFont", font.toString());
            qDebug() << "フォント設定を保存しました:" << font.toString();
        }
    }
}

int main(int argc, char *argv[])
{
    QApplication app(argc, argv);

    // QSettingsオブジェクトの初期化 (アプリケーション名と組織名を設定)
    g_settings = new QSettings("MyCompany", "MyApplication");

    g_label = new QLabel("Hello, Qt! This font is saved in settings.");
    g_label->setAlignment(Qt::AlignCenter);
    g_label->resize(500, 150);

    // 1. 保存されたフォント設定の読み込み
    QString savedFontString = g_settings->value("appFont", "").toString(); // キーが存在しない場合は空文字列を返す

    QFont loadedFont;
    if (!savedFontString.isEmpty() && loadedFont.fromString(savedFontString)) {
        qDebug() << "保存されたフォント設定を読み込みました:" << savedFontString;
        g_label->setFont(loadedFont);
    } else {
        qDebug() << "保存されたフォント設定が見つからないか、無効です。デフォルトフォントを使用します。";
        // デフォルトフォントを設定
        g_label->setFont(app.font());
    }

    // フォント選択ボタンの作成
    QPushButton *button = new QPushButton("フォントを選択...");
    QObject::connect(button, &QPushButton::clicked, onChooseFontButtonClicked);

    // レイアウトにQLabelとQPushButtonを追加
    QWidget window;
    QVBoxLayout *layout = new QVBoxLayout(&window);
    layout->addWidget(g_label);
    layout->addWidget(button);
    window.setLayout(layout);

    window.show();

    int result = app.exec();

    // QSettingsオブジェクトのクリーンアップ
    delete g_settings;
    g_settings = nullptr; // dangling pointerを避けるため
    
    return result;
}

解説

  • QFontDialog を使用してユーザーがフォントを選択できるようにし、選択されたフォントを QSettings に保存します。
  • 読み込んだ文字列が空でないことを確認してから loadedFont.fromString() を呼び出します。
  • 起動時に g_settings->value("appFont", "").toString() で設定を読み込みます。
  • QSettings を使用して、アプリケーション固有のフォント設定を永続的に保存します。

fromString() が失敗した場合のより堅牢なエラー処理と、代替フォントの使用例です。

#include <QApplication>
#include <QLabel>
#include <QFont>
#include <QString>
#include <QDebug>
#include <QFontInfo> // フォント情報の詳細取得用

int main(int argc, char *argv[])
{
    QApplication app(argc, argv);

    QString goodFontString = "Arial,14,-1,5,400,0,0,0,0,0,0,0,0,0,0,1"; // 有効なフォント文字列
    QString badFontString1 = "";                                      // 空の文字列
    QString badFontString2 = "InvalidFontString,10,0,0";              // 不完全な/無効な文字列
    QString nonexistentFontString = "NonExistentFontFamily,18";      // 存在しないフォントファミリー

    QLabel *label1 = new QLabel("Good Font String:");
    QLabel *label2 = new QLabel("Bad Font String (Empty):");
    QLabel *label3 = new QLabel("Bad Font String (Malformed):");
    QLabel *label4 = new QLabel("Non-existent Font Family:");

    QVBoxLayout *layout = new QVBoxLayout;

    // --- Case 1: 正常な文字列 ---
    QFont font1;
    if (font1.fromString(goodFontString)) {
        qDebug() << "Case 1: 'Good Font String' - 成功.";
        label1->setFont(font1);
    } else {
        qDebug() << "Case 1: 'Good Font String' - 失敗。デフォルトフォントを使用。";
        label1->setFont(app.font());
    }
    label1->setText(label1->text() + " " + label1->font().family() + " " + QString::number(label1->font().pointSize()) + "pt");
    layout->addWidget(label1);

    // --- Case 2: 空の文字列 ---
    QFont font2;
    if (font2.fromString(badFontString1)) {
        qDebug() << "Case 2: 'Bad Font String (Empty)' - 成功 (予期せぬ挙動)。";
        label2->setFont(font2);
    } else {
        qDebug() << "Case 2: 'Bad Font String (Empty)' - 失敗。デフォルトフォントを使用。";
        // 失敗した場合、デフォルトフォントを設定
        font2 = app.font();
        label2->setFont(font2);
    }
    label2->setText(label2->text() + " " + label2->font().family() + " " + QString::number(label2->font().pointSize()) + "pt");
    layout->addWidget(label2);

    // --- Case 3: 不完全な/無効な文字列 ---
    QFont font3;
    if (font3.fromString(badFontString2)) {
        qDebug() << "Case 3: 'Bad Font String (Malformed)' - 成功 (予期せぬ挙動)。";
        label3->setFont(font3);
    } else {
        qDebug() << "Case 3: 'Bad Font String (Malformed)' - 失敗。デフォルトフォントを使用。";
        // 失敗した場合、デフォルトフォントを設定
        font3 = app.font();
        label3->setFont(font3);
    }
    label3->setText(label3->text() + " " + label3->font().family() + " " + QString::number(label3->font().pointSize()) + "pt");
    layout->addWidget(label3);


    // --- Case 4: 存在しないフォントファミリー ---
    QFont font4;
    if (font4.fromString(nonexistentFontString)) {
        qDebug() << "Case 4: 'Non-existent Font Family' - 成功 (内部的に代替)。";
        label4->setFont(font4);
        // 実際に使用されるフォントファミリーを検証
        QFontInfo fontInfo(font4);
        qDebug() << "  実際に解決されたフォントファミリー:" << fontInfo.family();
        qDebug() << "  指定したフォントが正確に一致するか:" << fontInfo.exactMatch();
    } else {
        qDebug() << "Case 4: 'Non-existent Font Family' - 失敗 (予期せぬ挙動)。";
        font4 = app.font();
        label4->setFont(font4);
    }
    label4->setText(label4->text() + " " + label4->font().family() + " " + QString::number(label4->font().pointSize()) + "pt");
    layout->addWidget(label4);


    QWidget window;
    window.setLayout(layout);
    window.setWindowTitle("QFont::fromString() Error Handling Examples");
    window.show();

    return app.exec();
}
  • fromString()true を返しても、指定されたフォントファミリーがシステムに存在しない場合は、Qt が自動的に代替フォントを選択します。この場合、QFontInfo::exactMatch() を使用して、実際に指定したフォントが使用されているかどうかを確認できます。QFontInfo::family() で実際に使用されているフォントファミリーを取得できます。
  • fromString()false を返す場合(例: 空文字列や不正なフォーマット)、デフォルトのフォント (app.font()) を使用するなど、適切な代替処理を実装します。


QDataStream を使用する方法

QDataStream は、Qt のバイナリデータシリアライズフレームワークです。QFont クラスは QDataStream オペレータ (operator<<operator>>) を提供しているため、これを使って QFont オブジェクトを直接ストリームに書き込んだり、ストリームから読み込んだりできます。

メリット

  • バージョン管理
    ストリームのバージョンを制御することで、将来のフォーマット変更に対応できる。
  • Qtの標準的な方法
    Qtの他のデータ型(QPoint, QSize, QColor など)も同様にシリアライズ可能。
  • 効率的
    バイナリ形式で保存されるため、文字列ベースよりもサイズが小さく、読み書きが高速になる傾向がある。
  • タイプセーフ
    QDataStream は型情報を扱うため、誤った形式のデータを読み込むリスクが低い。

デメリット

  • 異なるシステム間での互換性
    QDataStream のバージョンやエンディアンに注意しないと、異なるシステム間で問題が発生する可能性がある。
  • 人間が読めない
    バイナリ形式なので、テキストエディタで開いても内容を理解できない。デバッグがしにくい場合がある。

使用例

#include <QApplication>
#include <QLabel>
#include <QFont>
#include <QFile>
#include <QDataStream>
#include <QDebug>
#include <QMessageBox>

const QString FONT_FILE = "font_settings.dat";

int main(int argc, char *argv[])
{
    QApplication app(argc, argv);

    // --- フォントの保存 ---
    QFont originalFont("Impact", 18, QFont::Bold, true);
    originalFont.setUnderline(true);

    QFile outFile(FONT_FILE);
    if (outFile.open(QIODevice::WriteOnly)) {
        QDataStream out(&outFile);
        out.setVersion(QDataStream::Qt_6_0); // 使用するQtのバージョンに合わせる
        out << originalFont;
        outFile.close();
        qDebug() << "フォント設定をバイナリファイルに保存しました: " << FONT_FILE;
    } else {
        qWarning() << "ファイルを開けませんでした: " << FONT_FILE;
    }

    // --- フォントの復元 ---
    QFont restoredFont;
    QFile inFile(FONT_FILE);
    if (inFile.open(QIODevice::ReadOnly)) {
        QDataStream in(&inFile);
        in.setVersion(QDataStream::Qt_6_0); // 保存時と同じバージョンを設定
        in >> restoredFont;
        inFile.close();
        qDebug() << "フォント設定をバイナリファイルから読み込みました。";
        qDebug() << "復元されたフォントファミリー: " << restoredFont.family();
        qDebug() << "復元されたフォントサイズ: " << restoredFont.pointSize();
    } else {
        qWarning() << "ファイルが見つからないか、開けませんでした: " << FONT_FILE;
        QMessageBox::warning(nullptr, "エラー", "フォント設定ファイルが見つかりません。");
        // ファイルが見つからない場合、デフォルトフォントを使用
        restoredFont = app.font();
    }

    QLabel label("Hello, QDataStream Font!");
    label.setFont(restoredFont);
    label.setAlignment(Qt::AlignCenter);
    label.resize(400, 100);
    label.show();

    return app.exec();
}

QVariant と QSettings を使用する方法

QVariant は、Qt の汎用データ型であり、さまざまなデータ型を保持できます。QFontQVariant に変換可能であり、QSettingsQVariant を直接保存・復元できます。これは、QFont::fromString() を使った方法よりもシンプルで、設定ファイルに保存する際に特に便利です。

メリット

  • QMLとの連携
    QVariant はQMLとC++間のデータ転送にも利用されるため、QMLベースのUIを持つアプリケーションで一貫性がある。
  • QSettings との親和性
    QSettingsQVariant をサポートしているため、設定管理が容易。
  • シンプル
    QFont::toString()QFont::fromString() を直接呼び出す必要がない。

デメリット

  • 内部的には QVariantQFont を文字列に変換(または他の内部表現に)している可能性があり、パフォーマンスや互換性において QDataStream に劣る可能性がある(ただし、通常の使用では問題にならない)。
  • QDataStream ほどの柔軟性はない
    複雑なカスタムシリアライズが必要な場合は向かない。

使用例

#include <QApplication>
#include <QLabel>
#include <QFont>
#include <QSettings>
#include <QDebug>
#include <QVariant> // QFontをQVariantとして扱うため

int main(int argc, char *argv[])
{
    QApplication app(argc, argv);

    QSettings settings("MyCompany", "MyApplicationV2"); // 組織名とアプリ名

    // --- フォントの保存 ---
    QFont originalFont("Verdana", 16);
    originalFont.setItalic(true);
    originalFont.setLetterSpacing(QFont::AbsoluteSpacing, 2); // 文字間隔も保存可能

    settings.setValue("userFont", originalFont); // QFontを直接QVariantとして保存
    qDebug() << "フォント設定をQSettingsに保存しました。";

    // --- フォントの復元 ---
    QFont restoredFont;
    // QVariantからQFontへ変換。デフォルト値としてQApplication::font()を使用
    restoredFont = settings.value("userFont", app.font()).value<QFont>();

    qDebug() << "QSettingsからフォント設定を読み込みました。";
    qDebug() << "復元されたフォントファミリー: " << restoredFont.family();
    qDebug() << "復元されたフォントサイズ: " << restoredFont.pointSize();
    qDebug() << "復元されたフォントがイタリックか: " << restoredFont.italic();

    QLabel label("Hello, QVariant Font!");
    label.setFont(restoredFont);
    label.setAlignment(Qt::AlignCenter);
    label.resize(400, 100);
    label.show();

    return app.exec();
}

XML や JSON を使用する方法 (手動シリアライズ)

より複雑な設定や、人間が読み書きできるフォーマットが必要な場合、XML (QXmlStreamWriter/Reader) や JSON (QJsonDocument/QJsonObject/QJsonArray) を使って QFont の各属性を個別にシリアライズ・デシリアライズする方法もあります。

メリット

  • 柔軟性
    フォント以外の設定も一緒に保存する際に、構造化されたデータとして扱える。
  • クロスプラットフォーム/クロス言語互換性
    多くのプログラミング言語でXML/JSONを解析できるため、異なるシステム間でのデータ交換に適している。
  • 人間が読める
    XMLやJSONはテキストベースなので、デバッグや手動での編集が容易。

デメリット

  • エラーハンドリング
    属性の読み込み順序や型のチェックなど、手動でのエラーハンドリングが必要。
  • 冗長性
    バイナリ形式に比べてファイルサイズが大きくなる傾向がある。
  • 実装が複雑
    QFont の各属性(ファミリー、サイズ、太さ、イタリック、下線など)を一つずつ手動で読み書きする必要がある。

使用例 (JSONの場合)

#include <QApplication>
#include <QLabel>
#include <QFont>
#include <QFile>
#include <QJsonDocument>
#include <QJsonObject>
#include <QDebug>

const QString JSON_FONT_FILE = "font_settings.json";

// QFontをQJsonObjectに変換する関数
QJsonObject fontToJson(const QFont& font) {
    QJsonObject obj;
    obj["family"] = font.family();
    obj["pointSize"] = font.pointSize();
    obj["weight"] = font.weight(); // QFont::Weight enumの整数値
    obj["italic"] = font.italic();
    obj["underline"] = font.underline();
    obj["strikeOut"] = font.strikeOut();
    // 他の属性も必要に応じて追加
    return obj;
}

// QJsonObjectからQFontを復元する関数
QFont jsonToFont(const QJsonObject& obj) {
    QFont font;
    font.setFamily(obj["family"].toString());
    font.setPointSize(obj["pointSize"].toInt());
    font.setWeight(static_cast<QFont::Weight>(obj["weight"].toInt()));
    font.setItalic(obj["italic"].toBool());
    font.setUnderline(obj["underline"].toBool());
    font.setStrikeOut(obj["strikeOut"].toBool());
    // 他の属性も必要に応じて設定
    return font;
}

int main(int argc, char *argv[])
{
    QApplication app(argc, argv);

    // --- フォントの保存 (JSON) ---
    QFont originalFont("Courier New", 10, QFont::Light, false);
    originalFont.setStrikeOut(true);

    QJsonObject fontObject = fontToJson(originalFont);
    QJsonDocument doc(fontObject);

    QFile outFile(JSON_FONT_FILE);
    if (outFile.open(QIODevice::WriteOnly | QIODevice::Text)) {
        outFile.write(doc.toJson());
        outFile.close();
        qDebug() << "フォント設定をJSONファイルに保存しました: " << JSON_FONT_FILE;
    } else {
        qWarning() << "JSONファイルを開けませんでした: " << JSON_FONT_FILE;
    }

    // --- フォントの復元 (JSON) ---
    QFont restoredFont;
    QFile inFile(JSON_FONT_FILE);
    if (inFile.open(QIODevice::ReadOnly | QIODevice::Text)) {
        QByteArray jsonData = inFile.readAll();
        inFile.close();

        QJsonDocument doc = QJsonDocument::fromJson(jsonData);
        if (doc.isObject()) {
            restoredFont = jsonToFont(doc.object());
            qDebug() << "JSONファイルからフォント設定を読み込みました。";
            qDebug() << "復元されたフォントファミリー: " << restoredFont.family();
            qDebug() << "復元されたフォントサイズ: " << restoredFont.pointSize();
        } else {
            qWarning() << "JSONファイルが不正な形式です。";
            restoredFont = app.font(); // エラー時はデフォルト
        }
    } else {
        qWarning() << "JSONファイルが見つからないか、開けませんでした: " << JSON_FONT_FILE;
        restoredFont = app.font(); // ファイルが見つからない場合はデフォルト
    }

    QLabel label("Hello, JSON Font!");
    label.setFont(restoredFont);
    label.setAlignment(Qt::AlignCenter);
    label.resize(400, 100);
    label.show();

    return app.exec();
}

どの方法を選ぶべきか?

  • QFont::fromString() を使い続ける場合
    シンプルな文字列ベースのシリアライズで十分な場合や、既存のコードベースがこの形式に依存している場合。
  • 人間が読める形式で、かつ他のシステムや言語との連携が必要な場合
    XMLやJSONによる手動シリアライズ。柔軟性が高いが、実装の手間が増える。
  • バイナリ形式で効率的に保存したい場合
    QDataStream。ファイルサイズや読み書き速度が重要な場合。
  • 最もシンプルでQt標準の推奨方法
    QVariantQSettings。特にユーザー設定として保存する場合。

プロジェクトの特定の要件を考慮して、最適な方法を選択してください。 Qt の QFont::fromString() は、QFont オブジェクトを文字列としてシリアライズ/デシリアライズする便利な方法ですが、代替手段もいくつか存在します。これらの方法は、データの永続化、ネットワーク経由でのデータ転送、または異なるアプリケーション間でのデータの共有など、特定のユースケースによって使い分けられます。

QDataStream を使用する (バイナリシリアライゼーション)

QDataStream は、Qt のオブジェクトや基本的なデータ型をバイナリ形式でシリアライズ/デシリアライズするための強力なツールです。QFontQDataStream と連携するように設計されており、operator<< (書き込み) と operator>> (読み込み) がオーバーロードされています。

メリット

  • 互換性
    QDataStream::setVersion() を使用することで、異なるQtバージョン間での前方・後方互換性をある程度管理できます。
  • 型安全性
    QDataStream は型の情報も扱うため、誤った型としてデータを読み込むリスクが低減されます。
  • 効率性
    テキストベースの toString() よりもコンパクトなバイナリ形式で保存されるため、ファイルサイズが小さくなり、読み書きが高速です。

デメリット

  • Qtエコシステム外での利用
    QDataStream はQt固有の形式であるため、Qtを使用しない他のアプリケーションやシステムとのデータ交換には適していません。
  • 人間による可読性がない
    バイナリ形式なので、テキストエディタで開いても内容を直接理解することはできません。デバッグが難しくなる場合があります。

使用例

#include <QApplication>
#include <QLabel>
#include <QFont>
#include <QFile>
#include <QDataStream>
#include <QDebug>

int main(int argc, char *argv[])
{
    QApplication app(argc, argv);

    QString fileName = "font_settings.dat";

    // --- フォントをファイルに保存 ---
    {
        QFile file(fileName);
        if (file.open(QIODevice::WriteOnly)) {
            QDataStream out(&file);
            out.setVersion(QDataStream::Qt_5_15); // バージョンを指定 (互換性のため重要)

            QFont originalFont("Segoe UI", 16, QFont::Bold, false);
            originalFont.setUnderline(true);
            
            out << originalFont; // QFontオブジェクトを直接ストリームに書き込む
            qDebug() << "フォントをバイナリファイルに保存しました:" << fileName;
            file.close();
        } else {
            qWarning() << "ファイルを開けませんでした:" << fileName;
        }
    }

    // --- フォントをファイルから復元 ---
    QFont restoredFont;
    {
        QFile file(fileName);
        if (file.open(QIODevice::ReadOnly)) {
            QDataStream in(&file);
            in.setVersion(QDataStream::Qt_5_15); // 保存時と同じバージョンを指定

            in >> restoredFont; // QFontオブジェクトをストリームから読み込む
            qDebug() << "フォントをバイナリファイルから復元しました:";
            qDebug() << "  ファミリー:" << restoredFont.family();
            qDebug() << "  サイズ:" << restoredFont.pointSize();
            qDebug() << "  太さ:" << restoredFont.weight();
            file.close();
        } else {
            qWarning() << "ファイルを開けませんでした:" << fileName;
            // エラー時のデフォルトフォント
            restoredFont = app.font();
        }
    }

    QLabel *label = new QLabel("Hello, Qt! This font was saved with QDataStream.");
    label->setFont(restoredFont);
    label->setAlignment(Qt::AlignCenter);
    label->resize(500, 100);
    label->show();

    return app.exec();
}

QVariant と QMetaType を使用する

QVariant は、Qt の様々なデータ型を格納できる汎用コンテナクラスです。QFontQVariant に直接格納できます。これは主に、プロパティシステムやモデル/ビュープログラミングで、異なる型のデータを統一的に扱う必要がある場合に便利です。

メリット

  • 簡単なAPI
    QVariant::fromValue()QVariant::value<T>() を使用して、変換を簡単に行えます。
  • 柔軟性
    異なる型のデータを同じコンテナで扱えるため、コードの柔軟性が向上します。
  • Qtのフレームワークとの統合
    QVariant はQtの多くの部分(QML、プロパティシステム、QSettingsなど)で広く使われています。

デメリット

  • パフォーマンス
    QDataStream ほどのパフォーマンスは期待できない場合があります。
  • シリアライゼーションの手段ではない
    QVariant 自体はシリアライゼーション形式を提供するものではありません。QSettings のように QVariant を扱うストレージメカニズムと組み合わせる必要があります。

使用例

#include <QApplication>
#include <QLabel>
#include <QFont>
#include <QVariant> // QVariant用
#include <QSettings> // QSettingsはQVariantを扱う
#include <QDebug>

int main(int argc, char *argv[])
{
    QApplication app(argc, argv);

    QSettings settings("MyCompany", "MyApplication_Variant");

    // 1. フォントをQVariantに格納し、QSettingsに保存
    {
        QFont originalFont("Verdana", 18, QFont::DemiBold, true);
        QVariant fontVariant = QVariant::fromValue(originalFont); // QFontをQVariantに変換

        settings.setValue("displayFont", fontVariant);
        qDebug() << "QVariant経由でフォント設定を保存しました。";
    }

    // 2. QSettingsからQVariantを読み込み、QFontに復元
    QFont restoredFont;
    QVariant loadedFontVariant = settings.value("displayFont"); // QVariantとして読み込む

    if (loadedFontVariant.isValid() && loadedFontVariant.canConvert<QFont>()) {
        restoredFont = loadedFontVariant.value<QFont>(); // QVariantからQFontに変換
        qDebug() << "QVariant経由でフォント設定を読み込みました:";
        qDebug() << "  ファミリー:" << restoredFont.family();
        qDebug() << "  サイズ:" << restoredFont.pointSize();
    } else {
        qDebug() << "保存されたフォント設定が見つからないか、無効です。デフォルトフォントを使用します。";
        restoredFont = app.font();
    }

    QLabel *label = new QLabel("Hello, Qt! This font was managed by QVariant.");
    label->setFont(restoredFont);
    label->setAlignment(Qt::AlignCenter);
    label->resize(500, 100);
    label->show();

    return app.exec();
}

QFont の各属性(フォントファミリー、サイズ、太さ、イタリックなど)を個別の値として保存し、読み込み時にそれらの値を使って新しい QFont オブジェクトを構築する方法です。

メリット

  • 非Qt環境との連携
    Qtを使用しない他のシステムでも簡単に解析・構築できます。
  • 柔軟なストレージ形式
    特定のフォーマット(INI、JSON、XML、CSVなど)に縛られず、アプリケーションのニーズに合わせて自由にデータを配置できます。
  • 高い可読性
    設定ファイル(INI, JSON, XMLなど)に保存する場合、各属性が明確に表示されるため、人間が読んで理解しやすいです。

デメリット

  • 一貫性
    属性の組み合わせによっては、QFont::fromString() が生成するような「完全な」フォント記述ではないため、システムが最適なフォントを選択するロジックが異なる場合があります。
  • メンテナンス性
    QFont に新しい属性が追加された場合、手動でコードを更新する必要があります。
  • 冗長性
    QFont の属性が多い場合、保存・復元コードが長くなります。
#include <QApplication>
#include <QLabel>
#include <QFont>
#include <QSettings>
#include <QDebug>

int main(int argc, char *argv[])
{
    QApplication app(argc, argv);

    QSettings settings("MyCompany", "MyApplication_ManualFont");

    // 1. フォントの各属性を個別に保存
    {
        QFont originalFont("Courier New", 10, QFont::Normal, false);
        originalFont.setFixedPitch(true); // 等幅フォントを設定
        originalFont.setStrikeOut(true);  // 取り消し線

        settings.setValue("font/family", originalFont.family());
        settings.setValue("font/pointSize", originalFont.pointSize());
        settings.setValue("font/weight", originalFont.weight());
        settings.setValue("font/italic", originalFont.italic());
        settings.setValue("font/underline", originalFont.underline());
        settings.setValue("font/strikeOut", originalFont.strikeOut());
        settings.setValue("font/fixedPitch", originalFont.fixedPitch());

        qDebug() << "フォントの各属性を個別に保存しました。";
    }

    // 2. 個別の属性を読み込み、QFontを構築
    QFont restoredFont;
    // 読み込み時にデフォルト値を指定することで、キーが存在しない場合のエラーを回避
    restoredFont.setFamily(settings.value("font/family", "Sans Serif").toString());
    restoredFont.setPointSize(settings.value("font/pointSize", 9).toInt());
    restoredFont.setWeight(settings.value("font/weight", QFont::Normal).toInt());
    restoredFont.setItalic(settings.value("font/italic", false).toBool());
    restoredFont.setUnderline(settings.value("font/underline", false).toBool());
    restoredFont.setStrikeOut(settings.value("font/strikeOut", false).toBool());
    restoredFont.setFixedPitch(settings.value("font/fixedPitch", false).toBool());

    qDebug() << "個別に保存された属性からフォントを復元しました:";
    qDebug() << "  ファミリー:" << restoredFont.family();
    qDebug() << "  サイズ:" << restoredFont.pointSize();
    qDebug() << "  太さ:" << restoredFont.weight();
    qDebug() << "  固定幅:" << restoredFont.fixedPitch();


    QLabel *label = new QLabel("Hello, Qt! This font was built from individual attributes.");
    label->setFont(restoredFont);
    label->setAlignment(Qt::AlignCenter);
    label->resize(500, 100);
    label->show();

    return app.exec();
}
  • 人間による可読性や非Qt環境とのデータ交換が必要な場合
    各属性を個別に保存し、JSONやXMLなどの標準的なテキスト形式を使用することを検討してください。この場合、Qtの QJsonDocumentQXmlStreamWriter/Reader を利用すると良いでしょう。
  • プロパティシステムやQMLとの連携が必要な場合
    QVariant が適しています。
  • パフォーマンスが重要で、バイナリ形式で良い場合
    QDataStream が最適です。ファイルサイズを小さく抑えたい場合や、大量のフォント設定を頻繁に保存・読み込みする場合に適しています。
  • 最もシンプルでQtに閉じている場合
    QFont::toString() / fromString() が非常に便利で推奨されます。特にQSettingsと組み合わせる場合は簡単です。