QFont::isCopyOf()

2025-06-06

QtプログラミングにおけるQFont::isCopyOf()関数は、2つのQFontオブジェクトが互いにコピーであるかどうかを判定するために使用されます。

具体的には、次のような意味を持ちます。

  • 物理的なフォントの一致: Qt内部では、QFontオブジェクトは実際に使用されるフォントを決定するために、システムにインストールされているフォントとマッチングを行います。isCopyOf()は、このマッチングの結果として、最終的に描画に用いられる物理的なフォントも同じであることを意味します。つまり、単に属性値が同じだけでなく、内部的に同じフォントデータを参照している、または同じフォントが選ばれる状態であるかを確認します。
  • 同一のフォント設定を共有しているか: isCopyOf()は、2つのQFontオブジェクトが、フォントファミリー、サイズ、太さ、イタリックなどのすべてのフォント属性において全く同じ設定を持っている場合にtrueを返します。

なぜこれが重要なのか?

Qtでは、QFontオブジェクトは「implicitly shared(暗黙的に共有される)」クラスの一つです。これは、複数のQFontオブジェクトが同じフォントデータを指している場合、そのデータを共有することでメモリ効率を向上させる仕組みです。

isCopyOf()は、この暗黙的な共有の特性を考慮して、2つのQFontオブジェクトが論理的にも物理的にも同じフォントを表しているかを確認するのに役立ちます。例えば、次のような状況で役立ちます。

  • 状態の確認: 例えば、あるウィジェットのフォント設定が、アプリケーション全体のデフォルトフォントと同じかどうかを確認したい場合など。
  • フォント比較: あるフォントが別のフォントと同一であるかどうかを厳密に比較したい場合に利用します。
  • キャッシュの最適化: 複数の場所で同じフォントが頻繁に使用される場合、isCopyOf()を使って既存のフォントオブジェクトのコピーであるかどうかをチェックし、重複するフォントオブジェクトの作成を避けることで、パフォーマンスを向上させることができます。

簡単なコード例

#include <QApplication>
#include <QFont>
#include <QDebug>

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

    QFont font1("Arial", 12, QFont::Normal, false);
    QFont font2("Arial", 12, QFont::Normal, false);
    QFont font3("Times New Roman", 12, QFont::Normal, false);
    QFont font4(font1); // font1のコピーとしてfont4を作成

    qDebug() << "font1 isCopyOf font2:" << font1.isCopyOf(font2); // おそらくtrue
    qDebug() << "font1 isCopyOf font3:" << font1.isCopyOf(font3); // false
    qDebug() << "font1 isCopyOf font4:" << font1.isCopyOf(font4); // true

    // 属性を少し変えてみる
    font2.setItalic(true);
    qDebug() << "font1 isCopyOf font2 (after change):" << font1.isCopyOf(font2); // false

    return a.exec();
}

この例では、font1font2は初期状態では同じ設定なのでisCopyOf()trueを返します。font3は異なるフォントなのでfalseです。font4font1のコピーとして作成されているため、trueを返します。font2の属性を変更すると、もはやfont1のコピーではなくなるためfalseになります。



QFont::isCopyOf() に関連する一般的なエラーとトラブルシューティング

    • 問題: QFont オブジェクトを操作しようとするとクラッシュしたり、意図しない挙動を示す。
    • 原因: QFont オブジェクトは、フォントシステムの機能を利用するため、QApplication (または QGuiApplication) インスタンスが事前に存在している必要があります。特に、フォントの解決やメトリクス情報の取得など、システムに依存する操作を行う際に問題が発生しやすいです。
    • トラブルシューティング:
      • main() 関数内で、QFont オブジェクトを作成したり操作したりする前に、必ず QApplication a(argc, argv); のような形で QApplication インスタンスを初期化していることを確認します。
  1. カスタムフォントのロードの問題

    • 問題: QFontDatabase::addApplicationFont() でカスタムフォントをロードしたにもかかわらず、isCopyOf() が期待通りに動作しない、またはフォントが適用されない。
    • 原因:
      • フォントIDとファミリー名の不一致: addApplicationFont() はフォントのIDを返しますが、QFont のコンストラクタに渡すのはフォントのファミリー名です。このファミリー名が、フォントファイル内に定義されている正式なファミリー名と一致していない場合、フォントが正しく解決されません。
      • ロードの失敗: addApplicationFont() が負の値を返す場合、フォントのロードに失敗しています。パスの誤りや、フォントファイルの破損などが考えられます。
      • ロードのタイミング: addApplicationFont()QApplication インスタンスが作成された後で呼び出す必要があります。
    • トラブルシューティング:
      • addApplicationFont() の戻り値を確認し、ロードが成功したか(IDが0以上か)を確認します。
      • QFontDatabase::applicationFontFamilies(fontId) を使って、ロードしたフォントの正式なファミリー名を取得し、それを QFont コンストラクタに渡します。
      • フォントファイルのパスが正しいか、ファイルが実際に存在するかを確認します。
      • カスタムフォントをテストするために、シンプルな QLabel などにフォントを適用してみて、正しく表示されるかを確認します。
  2. QFont のポインタと参照の混同 (直接的な isCopyOf() のエラーではないが関連するQFont使用時のエラー)

    • 問題: setFont() などで QFont を設定しようとするとコンパイルエラーになる。
    • 原因: QFont オブジェクトをポインタで渡しがちですが、多くのQt関数は const QFont& を期待します。
    • トラブルシューティング:
      • QFont* font; の代わりに QFont font; を使用するか、ポインタを使用する場合は *font のようにデリファレンスして渡すことを確認します。
  • シンプルな再現コードの作成: 複雑なアプリケーションの一部で問題が発生している場合、isCopyOf() の挙動だけを切り出した最小限のコードを作成し、問題を再現できるか試します。これにより、問題の範囲を絞り込むことができます。
  • qDebug() を多用する: 比較している両方の QFont オブジェクトのすべての主要な属性 (family(), pointSize(), weight(), italic(), stretch(), fixedPitch(), underline(), strikeOut()) を qDebug() で出力し、どこに違いがあるのかを視覚的に確認します。


QFont::isCopyOf() は、2つの QFont オブジェクトが全く同じフォント設定(フォントファミリー、サイズ、太さ、イタリックなどすべての属性)を持っているか、そして内部的に同じフォントデータを参照しているかを厳密に比較する際に使用されます。

例1: 基本的な比較

この例では、異なる方法で作成された QFont オブジェクトが isCopyOf() でどのように比較されるかを示します。

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

int main(int argc, char *argv[])
{
    QApplication a(argc, argv); // QApplicationのインスタンスは必須

    qDebug() << "--- 例1: 基本的な比較 ---";

    // 1. 全く同じ属性を持つフォントオブジェクト
    QFont font1("Arial", 12, QFont::Normal, false); // Arial, 12pt, 通常, 非イタリック
    QFont font2("Arial", 12, QFont::Normal, false); // font1と全く同じ設定

    qDebug() << "font1 isCopyOf font2:" << font1.isCopyOf(font2);
    // 期待される出力: true (属性が全て同じため)

    // 2. 属性が一部異なるフォントオブジェクト
    QFont font3("Arial", 14, QFont::Normal, false); // サイズが異なる
    qDebug() << "font1 isCopyOf font3 (サイズ違い):" << font1.isCopyOf(font3);
    // 期待される出力: false

    QFont font4("Arial", 12, QFont::Bold, false); // 太さが異なる
    qDebug() << "font1 isCopyOf font4 (太さ違い):" << font1.isCopyOf(font4);
    // 期待される出力: false

    QFont font5("Times New Roman", 12, QFont::Normal, false); // フォントファミリーが異なる
    qDebug() << "font1 isCopyOf font5 (フォントファミリー違い):" << font1.isCopyOf(font5);
    // 期待される出力: false

    // 3. 既存のフォントオブジェクトからコピーコンストラクタで作成
    QFont font6(font1); // font1のコピーとしてfont6を作成
    qDebug() << "font1 isCopyOf font6 (コピーコンストラクタ):" << font1.isCopyOf(font6);
    // 期待される出力: true

    return a.exec();
}

解説

  • font6font1 をコピーコンストラクタで生成しているため、もちろん isCopyOf()true を返します。
  • font3font4font5font1 と何らかの属性が異なるため、isCopyOf()false を返します。
  • font1font2 は、コンストラクタで全く同じ属性を指定しているため、isCopyOf()true を返します。

例2: QFontInfo との組み合わせ、および内部的な解像度の違い

この例では、QFont::isCopyOf() がどのように厳密な比較を行うか、特に浮動小数点数のフォントサイズ設定や、QFontInfo を使って実際に解決されたフォント情報を確認する方法を示します。

#include <QApplication>
#include <QFont>
#include <QDebug>
#include <QFontInfo> // フォント情報取得用

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

    qDebug() << "\n--- 例2: QFontInfo と解像度の違い ---";

    QFont fontA("Roboto", 12, QFont::Normal, false);
    QFont fontB("Roboto", 12, QFont::Normal, false);

    qDebug() << "fontA isCopyOf fontB:" << fontA.isCopyOf(fontB);
    // 期待される出力: true

    // 小数点以下のサイズを指定してみる (Qt 内部ではより高精度で扱われることがある)
    QFont fontC;
    fontC.setFamily("Roboto");
    fontC.setPointSizeF(12.0); // 12.0pt を設定
    fontC.setWeight(QFont::Normal);
    fontC.setItalic(false);

    QFont fontD;
    fontD.setFamily("Roboto");
    fontD.setPointSizeF(12.000001); // わずかに異なるサイズを設定
    fontD.setWeight(QFont::Normal);
    fontD.setItalic(false);

    qDebug() << "fontC isCopyOf fontD (微妙なサイズ違い):" << fontC.isCopyOf(fontD);
    // 期待される出力: false (内部的に少しでもサイズが異なればfalse)

    // QFontInfo を使って実際に解決されたフォント情報を確認
    QFontInfo infoC(fontC);
    QFontInfo infoD(fontD);

    qDebug() << "fontC (resolved): " << infoC.family() << infoC.pointSize() << infoC.weight() << infoC.italic();
    qDebug() << "fontD (resolved): " << infoD.family() << infoD.pointSize() << infoD.weight() << infoD.italic();
    // ほとんど同じように見えるかもしれませんが、内部的には異なる場合があります。

    // 別のフォントで比較
    QFont fontE("Consolas", 10);
    QFont fontF("Consolas", 10);

    qDebug() << "fontE isCopyOf fontF:" << fontE.isCopyOf(fontF);
    // 期待される出力: true

    return a.exec();
}

解説

  • QFontInfo を使用することで、QFont オブジェクトがシステム上で実際に解決された(つまり、描画に使われる)フォントの具体的な情報を取得できます。これにより、見た目には同じでも内部的に異なる点がないかをデバッグするのに役立ちます。
  • setPointSizeF() を使用して小数点以下の精度でフォントサイズを設定した場合、isCopyOf() は内部的な精度での比較を行うため、わずかな違いでも false を返すことがあります。これは、QFont::isCopyOf() が非常に厳密な比較を行うことを示しています。

isCopyOf() は、フォントオブジェクトの重複作成を避けることで、パフォーマンスを最適化するシナリオで役立ちます。

#include <QApplication>
#include <QFont>
#include <QDebug>
#include <QHash> // フォントのキャッシュ用

// アプリケーション全体で共有されるフォントキャッシュ
QHash<QString, QFont> fontCache;

// フォントを取得するヘルパー関数
QFont getCachedFont(const QString& family, int pointSize, QFont::Weight weight, bool italic)
{
    // キャッシュを走査して、同じ設定のフォントが既に存在するか確認
    for (auto it = fontCache.constBegin(); it != fontCache.constEnd(); ++it) {
        QFont existingFont = it.value();
        // 取得したいフォント設定と同じかどうかを isCopyOf() で厳密にチェック
        QFont requestedFont(family, pointSize, weight, italic);

        if (requestedFont.isCopyOf(existingFont)) {
            qDebug() << "キャッシュから既存のフォントを返します。";
            return existingFont; // 既存のフォントを返す
        }
    }

    // キャッシュになければ新しく作成し、キャッシュに追加
    qDebug() << "新しいフォントを作成し、キャッシュに追加します。";
    QFont newFont(family, pointSize, weight, italic);
    fontCache.insert(family + QString::number(pointSize) + QString::number(weight) + QString::number(italic), newFont);
    return newFont;
}

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

    qDebug() << "\n--- 例3: フォントのキャッシュと最適化 ---";

    QFont myFont1 = getCachedFont("Verdana", 10, QFont::Normal, false);
    qDebug() << "My Font 1 Family:" << myFont1.family();

    QFont myFont2 = getCachedFont("Verdana", 10, QFont::Normal, false); // myFont1と全く同じ設定
    qDebug() << "My Font 2 Family:" << myFont2.family();

    QFont myFont3 = getCachedFont("Verdana", 12, QFont::Normal, false); // サイズが異なる
    qDebug() << "My Font 3 Family:" << myFont3.family();

    QFont myFont4 = getCachedFont("Verdana", 10, QFont::Normal, false); // 再びmyFont1と同じ設定
    qDebug() << "My Font 4 Family:" << myFont4.family();

    // キャッシュされたフォントオブジェクトが同じかどうか確認 (メモリ上のアドレスは異なる可能性があるが、内容は同じ)
    // QFont は暗黙的に共有されるため、実際に参照されるフォントデータは同じになる
    qDebug() << "myFont1 isCopyOf myFont2:" << myFont1.isCopyOf(myFont2); // true
    qDebug() << "myFont1 isCopyOf myFont3:" << myFont1.isCopyOf(myFont3); // false
    qDebug() << "myFont1 isCopyOf myFont4:" << myFont1.isCopyOf(myFont4); // true

    return a.exec();
}
  • myFont2myFont4 の取得時には、「キャッシュから既存のフォントを返します。」というメッセージが表示されるはずです。
  • これにより、同じフォント設定の QFont オブジェクトが何度も作成されるのを防ぎ、メモリ使用量を削減し、フォント解決のオーバーヘッドを減らすことができます。
  • もし存在すれば既存のオブジェクトを返し、存在しなければ新しいオブジェクトを作成してキャッシュに追加します。
  • この例では、getCachedFont というヘルパー関数を使って、アプリケーションがフォントを要求した際に、既に同じフォント設定の QFont オブジェクトがキャッシュに存在するかどうかを isCopyOf() でチェックしています。


各フォント属性を個別に比較する

最も直接的な代替方法は、QFont オブジェクトの各属性を個別に比較することです。これは isCopyOf() が内部的に行っていることのより詳細なバージョンです。

目的: