Qtプログラミング: QFont::strikeOut()でテキストに打ち消し線を引く方法

2025-06-06

QFont::strikeOut() とは

QFont::strikeOut()は、QtフレームワークのQFontクラスに属する関数です。この関数は、フォントに**打ち消し線(取り消し線、ストライクアウト)**を適用するかどうかを設定するために使用されます。

具体的には、テキストの上に水平な線を描画することで、そのテキストが取り消されたり、完了したりしたことを視覚的に示すことができます。

使用方法

QFont::strikeOut()にはブール値(trueまたはfalse)を渡します。

  • font.setStrikeOut(false);
    • このフォントを使用するテキストの打ち消し線が無効になります(デフォルトの状態)。
  • font.setStrikeOut(true);
    • このフォントを使用するテキストに打ち消し線が描画されるようになります。

コード例

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

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

    QLabel label;
    label.setText("これは打ち消し線のあるテキストです。");

    // 現在のフォントを取得
    QFont font = label.font();

    // 打ち消し線を有効にする
    font.setStrikeOut(true);

    // フォントをラベルに設定
    label.setFont(font);

    label.show();

    return app.exec();
}

このコードを実行すると、「これは打ち消し線のあるテキストです。」というテキストに打ち消し線が引かれて表示されます。

  • フォントの他の属性(太さ、斜体、下線など)と同様に、strikeOutもフォントマッチングアルゴリズムによって、システムで利用可能な最も近いフォントに適用される可能性があります。
  • QFont::strikeOut()は、QFontオブジェクトの属性を設定する関数です。実際の描画は、このQFontオブジェクトがQLabelQPushButtonなどのウィジェット、あるいはQPainterによってテキストを描画する際に適用されます。


QFont::strikeOut() 自体は非常にシンプルな関数であり、直接的なエラーを引き起こすことは稀です。しかし、フォントの適用全般に関連する問題や、意図したように打ち消し線が表示されないといった状況は発生し得ます。

打ち消し線が表示されない、または期待通りに表示されない

原因とトラブルシューティング

  • 描画イベントの不足
    フォントを変更した後、ウィジェットが再描画されていない可能性があります。 解決策: update() または repaint() を呼び出して、ウィジェットの再描画を強制してみてください。

  • Rich Text (QTextEditなど) の場合
    QTextEdit のようなリッチテキストを扱うウィジェットでは、QFont オブジェクト全体を直接設定するよりも、QTextCharFormat を使用してテキストの一部にスタイルを適用する方が一般的です。 QTextCharFormat には setFontStrikeOut() 関数があります。 例:

    QTextCursor cursor = textEdit->textCursor();
    QTextCharFormat format = cursor.charFormat();
    format.setFontStrikeOut(true);
    cursor.setCharFormat(format);
    

    解決策
    QTextEdit でテキストの特定の部分に打ち消し線を適用したい場合は、QTextCharFormat::setFontStrikeOut() を使用してください。

  • 互換性のないフォント
    ごく稀に、特定のフォントが打ち消し線に対応していない、またはレンダリングが正しく行われない場合があります。特に、非常に特殊なフォントや、システムに正しくインストールされていないフォントで発生する可能性があります。 解決策: 別の一般的なフォント(例: Arial, Times New Roman, Yu Gothic など)で試してみて、問題が解消するかどうかを確認してください。

  • スタイルシートの使用
    Qt のスタイルシートを使用している場合、ウィジェットのフォント設定はスタイルシートによって上書きされることがあります。スタイルシートでフォントが明示的に定義されている場合、C++ コードで setFont() を呼び出しても効果がないことがあります。 例:

    QLabel {
        font-family: "Arial";
        font-size: 12pt;
        /* text-decoration: line-through; がない場合、表示されない */
    }
    

    解決策
    スタイルシートで打ち消し線 (text-decoration: line-through;) を設定するか、C++ コードで setFont() を使用する場合は、スタイルシートを一時的に無効にするか、特定のウィジェットにのみスタイルシートを適用するなど、スタイルシートの適用範囲を確認してください。

  • フォントがウィジェットに適用されていない
    QFont オブジェクトを変更しても、それが実際に表示されるウィジェット(QLabel, QPushButton など)に設定されていないと、変更は反映されません。 例:

    QFont font = someWidget->font(); // ウィジェットの現在のフォントを取得
    font.setStrikeOut(true);      // 打ち消し線を有効に
    // someWidget->setFont(font); // これを忘れると適用されない!
    

    解決策
    setFont() を呼び出して、変更した QFont オブジェクトをウィジェットに再設定してください。

コンパイルエラー

QFont::strikeOut() 自体のコンパイルエラーはほとんどありませんが、QFont オブジェクトの渡し方に関連するエラーはよく見られます。

  • setFont(QFont*) ではなく setFont(const QFont&)
    これは QFont::strikeOut() に直接関係するエラーではありませんが、QFont オブジェクトをウィジェットに設定する際によくある間違いです。 誤った例:
    QFont *font = new QFont();
    font->setStrikeOut(true);
    // label->setFont(font); // エラー: QFont* を QFont& に変換できない
    
    解決策: QFont オブジェクトは通常、値渡しまたは参照渡しされます。ポインタを渡す場合は、逆参照 (*font) を使用する必要があります。
    QFont font; // スタックにQFoontオブジェクトを作成
    font.setStrikeOut(true);
    label->setFont(font); // 値渡し
    
    // または
    QFont *font = new QFont(); // ヒープにQFontオブジェクトを作成
    font->setStrikeOut(true);
    label->setFont(*font); // 逆参照して渡す
    delete font; // ヒープに作成した場合は必ず解放
    
  • パフォーマンス
    頻繁にフォントを変更すると、わずかながらパフォーマンスに影響を与える可能性があります。ただし、通常のアプリケーションではほとんど問題になりません。

  • プラットフォームの違い
    OS やフォントレンダリングエンジンの違いにより、打ち消し線の太さや位置が微妙に異なる場合があります。これは Qt の問題というよりは、OS レベルのレンダリングの差に起因することが多いです。 解決策: 特にない場合が多いですが、必要であれば QPainter を使用してカスタムで線を描画することで、より細かい制御が可能です。



例1: QLabel に打ち消し線を適用する

最も基本的な使用例です。QLabel ウィジェットのテキストに静的に打ち消し線を適用します。

#include <QApplication>
#include <QLabel>
#include <QFont>
#include <QVBoxLayout> // ウィジェットの配置のため

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

    // メインウィンドウを作成
    QWidget window;
    window.setWindowTitle("QFont::strikeOut() Example 1");

    // QLabel を作成
    QLabel *label = new QLabel("このテキストには打ち消し線が引かれます。");

    // 現在のフォントを取得
    QFont font = label->font();

    // 打ち消し線を有効にする
    font.setStrikeOut(true);

    // 変更したフォントを QLabel に設定
    label->setFont(font);

    // レイアウトに QLabel を追加
    QVBoxLayout *layout = new QVBoxLayout(&window);
    layout->addWidget(label);

    window.show();

    return app.exec();
}

解説

  1. QLabel オブジェクトを作成します。
  2. label->font() を呼び出して、現在のフォント設定を取得します。これは既存のフォントのコピーを返します。
  3. 取得した QFont オブジェクトの setStrikeOut(true) を呼び出し、打ち消し線を有効にします。
  4. 変更された QFont オブジェクトを label->setFont(font)QLabel に設定し直します。これにより、ウィジェットの表示が更新されます。

例2: QPushButton で打ち消し線をトグルする

ボタンをクリックするたびに、別の QLabel の打ち消し線を有効/無効に切り替える例です。

#include <QApplication>
#include <QWidget>
#include <QLabel>
#include <QPushButton>
#include <QVBoxLayout>
#include <QHBoxLayout>

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

    QWidget window;
    window.setWindowTitle("QFont::strikeOut() Example 2");

    QLabel *textLabel = new QLabel("このテキストの打ち消し線が変わります。");
    QPushButton *toggleButton = new QPushButton("打ち消し線をトグル");

    // ラベルの初期フォントを取得
    QFont labelFont = textLabel->font();
    // 打ち消し線の初期状態を保存 (trueまたはfalse)
    bool isStrikeOutEnabled = false; 

    // ボタンがクリックされたときの処理 (Lambda関数を使用)
    QObject::connect(toggleButton, &QPushButton::clicked, [&]() {
        isStrikeOutEnabled = !isStrikeOutEnabled; // 状態を反転
        labelFont.setStrikeOut(isStrikeOutEnabled); // フォントに打ち消し線状態を適用
        textLabel->setFont(labelFont); // ラベルにフォントを再設定
    });

    QVBoxLayout *mainLayout = new QVBoxLayout(&window);
    QHBoxLayout *buttonLayout = new QHBoxLayout();

    buttonLayout->addStretch(); // ボタンを中央に寄せるため
    buttonLayout->addWidget(toggleButton);
    buttonLayout->addStretch();

    mainLayout->addWidget(textLabel, 0, Qt::AlignCenter); // テキストを中央に配置
    mainLayout->addLayout(buttonLayout);

    window.show();

    return app.exec();
}

解説

  1. QLabelQPushButton を作成します。
  2. QLabel の現在のフォントを取得し、打ち消し線の状態を保持するブール変数 isStrikeOutEnabled を用意します。
  3. QPushButtonclicked シグナルをラムダ関数に接続します。
  4. ラムダ関数内で、isStrikeOutEnabled の状態を反転させ、その新しい状態を labelFont.setStrikeOut() に渡します。
  5. 最後に、更新された labelFonttextLabel->setFont() で再設定し、表示を更新します。

例3: QTextEdit で選択範囲または現在のカーソル位置に打ち消し線を適用する

QTextEdit のようなリッチテキストを扱うウィジェットでは、QTextCharFormat を使用してテキストの一部にスタイルを適用するのが一般的です。

#include <QApplication>
#include <QWidget>
#include <QTextEdit>
#include <QPushButton>
#include <QVBoxLayout>
#include <QTextCursor>
#include <QTextCharFormat>

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

    QWidget window;
    window.setWindowTitle("QFont::strikeOut() Example 3 (QTextEdit)");

    QTextEdit *textEdit = new QTextEdit(&window);
    textEdit->setPlainText("ここにテキストを入力し、一部を選択して打ち消し線を適用できます。\n"
                           "または、カーソルを置いてからボタンをクリックすると、入力するテキストに打ち消し線が適用されます。");

    QPushButton *strikeOutButton = new QPushButton("選択/入力に打ち消し線をトグル");

    QObject::connect(strikeOutButton, &QPushButton::clicked, [&]() {
        QTextCursor cursor = textEdit->textCursor();
        QTextCharFormat format = cursor.charFormat(); // 現在の文字フォーマットを取得

        // 打ち消し線の現在の状態を取得し、反転させる
        bool currentStrikeOut = format.fontStrikeOut();
        format.setFontStrikeOut(!currentStrikeOut);

        // 選択範囲がある場合は、その範囲にフォーマットを適用
        if (cursor.hasSelection()) {
            cursor.mergeCharFormat(format); // 選択範囲に新しいフォーマットを適用
        } else {
            // 選択範囲がない場合は、現在のカーソル位置以降に入力されるテキストにフォーマットを適用
            textEdit->setCurrentCharFormat(format);
        }
    });

    QVBoxLayout *layout = new QVBoxLayout(&window);
    layout->addWidget(textEdit);
    layout->addWidget(strikeOutButton);

    window.show();

    return app.exec();
}
  1. QTextEditQPushButton を作成します。
  2. ボタンがクリックされたら、textEdit->textCursor() で現在のカーソルを取得します。
  3. cursor.charFormat() で現在の文字フォーマット(QTextCharFormat)を取得します。
  4. format.fontStrikeOut() で現在の打ち消し線の状態を取得し、format.setFontStrikeOut() でその状態を反転させて設定します。
  5. cursor.hasSelection() でテキストが選択されているかどうかを確認します。
    • 選択されている場合
      cursor.mergeCharFormat(format) を使用して、選択範囲に新しいフォーマットを適用します。
    • 選択されていない場合
      textEdit->setCurrentCharFormat(format) を使用して、現在のカーソル位置以降に入力されるテキストに新しいフォーマットを適用します。


QFont::strikeOut() の代替方法

主に以下の3つのアプローチが考えられます。

  1. Qt のスタイルシート (CSS) を利用する
  2. QPainter を使用して手動で描画する
  3. QTextDocument/QTextCharFormat を利用する (リッチテキストの場合)

Qt のスタイルシート (CSS) を利用する

Qt のスタイルシートは、HTML/CSS に似た構文でウィジェットの外観をカスタマイズする強力な機能です。テキストの装飾もこれに含まれます。

メリット

  • 動的な変更が容易
    実行時にスタイルシートを切り替えることで、見た目を簡単に変更できる。
  • 柔軟性
    アプリケーション全体または特定のウィジェットに適用でき、テーマ設定にも便利。
  • 宣言的
    コードから見た目の設定が分離され、管理しやすい。

デメリット

  • C++ コードから直接 QFont を操作するよりも、間接的な設定になる。

使用例

#include <QApplication>
#include <QLabel>
#include <QPushButton> // スタイルシートの適用例として
#include <QVBoxLayout>

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

    QWidget window;
    window.setWindowTitle("Stylesheet StrikeOut Example");

    QLabel *label1 = new QLabel("CSSで打ち消し線が適用されるテキストです。");
    QLabel *label2 = new QLabel("通常のテキストです。");
    QPushButton *button = new QPushButton("ボタンにも適用可能");

    // label1 にスタイルシートで打ち消し線を適用
    label1->setStyleSheet("QLabel { text-decoration: line-through; color: blue; }");

    // button にスタイルシートで打ち消し線を適用
    button->setStyleSheet("QPushButton { text-decoration: line-through; font-weight: bold; }");

    QVBoxLayout *layout = new QVBoxLayout(&window);
    layout->addWidget(label1);
    layout->addWidget(label2);
    layout->addWidget(button);

    window.show();

    return app.exec();
}

解説
text-decoration: line-through; プロパティをスタイルシートに記述することで、打ち消し線が適用されます。color, font-weight など、他のCSSプロパティと組み合わせて使用できます。

QPainter を使用して手動で描画する

ウィジェットの paintEvent() 関数内で QPainter を使用して、テキストを描画し、その上に自分で線を描画する方法です。これは最も柔軟性が高く、線の位置、太さ、色、スタイルなどを完全に制御できます。

メリット

  • 特定の描画要件(例:テキストの一部にだけ線を引く、複数の線を引くなど)に対応できる。
  • 完全な制御
    線の描画に関するあらゆる側面(位置、太さ、色、スタイルなど)を自由にカスタマイズできる。

デメリット

  • テキストの配置やフォントメトリックを正確に計算する必要がある。
  • QFont::strikeOut() に比べてコード量が増え、複雑になる。

使用例

#include <QApplication>
#include <QWidget>
#include <QPainter>
#include <QFontMetrics> // テキストのサイズ測定のため

class CustomStrikeOutWidget : public QWidget
{
public:
    CustomStrikeOutWidget(QWidget *parent = nullptr) : QWidget(parent)
    {
        setText("カスタム描画で打ち消し線。");
        setFont(QFont("Arial", 20)); // フォントを設定
    }

    void setText(const QString &text) { m_text = text; update(); }

protected:
    void paintEvent(QPaintEvent *event) override
    {
        Q_UNUSED(event);
        QPainter painter(this);
        painter.setRenderHint(QPainter::Antialiasing); // アンチエイリアス有効化

        // (1) 通常のテキストを描画
        painter.setFont(font()); // ウィジェットのフォントを使用
        painter.setPen(Qt::black); // テキストの色
        
        // テキストの描画位置を計算(中央揃えの例)
        QFontMetrics fm(painter.font());
        int textWidth = fm.horizontalAdvance(m_text);
        int textHeight = fm.height();
        int x = (width() - textWidth) / 2;
        int y = (height() + textHeight) / 2 - fm.descent(); // ベースラインに合わせる

        painter.drawText(x, y, m_text);

        // (2) 打ち消し線を描画
        QPen strikePen(Qt::red); // 線の色
        strikePen.setWidth(2);   // 線の太さ
        painter.setPen(strikePen);

        // 打ち消し線の位置を計算
        // 通常、打ち消し線はテキストのベースラインより上に引かれる
        // QFontMetrics::xHeight() や QFontMetrics::ascent() を参考に位置を調整
        // ここでは簡単にテキストの高さの中央付近に線を引く例
        int lineHeight = fm.height();
        int strikeY = y - lineHeight / 2; // テキストの高さの半分をベースラインから引く

        painter.drawLine(x, strikeY, x + textWidth, strikeY);
    }

private:
    QString m_text;
};

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

    CustomStrikeOutWidget window;
    window.resize(400, 200);
    window.show();

    return app.exec();
}

解説

  1. paintEvent() をオーバーライドし、QPainter オブジェクトを作成します。
  2. painter.drawText() で通常のテキストを描画します。
  3. QFontMetrics を使用して、描画するテキストの幅や高さ、ベースラインなどのフォントメトリック情報を取得します。これにより、線の正確な位置を計算できます。
  4. QPen を設定して、打ち消し線の色や太さを決めます。
  5. painter.drawLine() で、計算した位置に打ち消し線を描画します。

QTextDocument / QTextCharFormat を利用する (リッチテキストの場合)

QTextEditQTextBrowser のようなリッチテキストを扱うウィジェットでは、QTextCharFormat を使用してテキストの書式設定を行います。QFont::strikeOut() は内部的に QTextCharFormat::setFontStrikeOut() を使用しています。

メリット

  • HTMLなどのリッチテキスト形式との連携が容易。
  • リッチテキスト内の特定のテキスト範囲にスタイルを適用できる。

デメリット

  • プレーンテキストのみを表示する単純なウィジェット (QLabel など) には直接適用できない。
#include <QApplication>
#include <QTextEdit>
#include <QVBoxLayout>
#include <QPushButton>
#include <QTextCursor>
#include <QTextCharFormat>

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

    QWidget window;
    window.setWindowTitle("QTextEdit StrikeOut Example");

    QTextEdit *textEdit = new QTextEdit(&window);
    textEdit->setPlainText("これはテキストエディタのテキストです。一部を選択して打ち消し線を適用します。");

    QPushButton *toggleButton = new QPushButton("選択範囲に打ち消し線をトグル");

    QObject::connect(toggleButton, &QPushButton::clicked, [&]() {
        QTextCursor cursor = textEdit->textCursor();
        if (!cursor.hasSelection()) {
            return; // 選択されていない場合は何もしない
        }

        QTextCharFormat format = cursor.charFormat();
        bool currentStrikeOut = format.fontStrikeOut();
        format.setFontStrikeOut(!currentStrikeOut); // 状態を反転

        cursor.mergeCharFormat(format); // 選択範囲に新しいフォーマットを適用
    });

    QVBoxLayout *layout = new QVBoxLayout(&window);
    layout->addWidget(textEdit);
    layout->addWidget(toggleButton);

    window.show();

    return app.exec();
}
  1. QTextEdit のテキストを選択します。
  2. ボタンをクリックすると、選択範囲の QTextCharFormat を取得し、setFontStrikeOut() を呼び出して打ち消し線の状態を切り替えます。
  3. cursor.mergeCharFormat(format) で変更を適用します。
  • リッチテキストエディタの機能
    QTextEdit のようなリッチテキストウィジェットで、ユーザーがテキストに打ち消し線を適用できるようにしたい場合は、QTextCharFormat を使用するのが自然です。
  • 高度なカスタマイズが必要な場合
    打ち消し線の位置、太さ、色、複数線など、Qt の標準機能では表現できない複雑な描画が必要な場合は、QPainter を使用したカスタム描画が唯一の選択肢となります。
  • 見た目の統一や外部からの設定
    アプリケーション全体のスタイルを統一したい、またはデザイナーが CSS で見た目を定義したい場合は、スタイルシートが非常に強力です。
  • 最も簡単で標準的な方法
    ほとんどの場合、QFont::strikeOut() を直接使用するのが最もシンプルで推奨される方法です。