You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Qt:如何在保持对齐的前提下使用不同字体绘制文本

Great question! The built-in QPainter::drawText method works perfectly for single-font text alignment, but when you need mixed fonts while preserving that same alignment behavior, you’ve got two solid approaches to choose from. Let’s break them down:

Method 1: Manual Position Calculation (Ideal for Simple Single-Line Text)

This approach gives you full control over every text segment’s position, ensuring the entire block aligns within your target rectangle just like the original drawText method would. Here’s how to implement it:

Step-by-Step Logic

  1. Split your text into segments, each paired with its desired font.
  2. Calculate the size of each segment using QFontMetricsF.
  3. Compute the total width of all segments combined, plus the maximum height across all fonts (to handle vertical alignment correctly).
  4. Use the original QTextOption alignment to calculate the baseline starting position for the entire text block.
  5. Draw each segment sequentially, updating the x-position after each draw to keep text continuous.

Code Example

#include <QPainter>
#include <QFontMetricsF>
#include <QList>
#include <QPair>

void drawMultiFontText(QPainter *painter, const QRectF &rect, 
                       const QList<QPair<QString, QFont>> &textFragments, 
                       const QTextOption &option = QTextOption())
{
    if (textFragments.isEmpty() || !painter) return;

    // Calculate metrics for each text segment
    QList<QFontMetricsF> metricsList;
    qreal totalWidth = 0;
    qreal maxHeight = 0;

    for (const auto &fragment : textFragments) {
        QFontMetricsF metrics(fragment.second);
        metricsList.append(metrics);
        totalWidth += metrics.horizontalAdvance(fragment.first);
        maxHeight = qMax(maxHeight, metrics.height());
    }

    // Calculate starting baseline position based on alignment
    qreal baseX = rect.left();
    qreal baseY = rect.top();

    // Handle horizontal alignment
    Qt::Alignment hAlign = option.alignment() & Qt::AlignHorizontal_Mask;
    switch (hAlign) {
        case Qt::AlignHCenter:
            baseX = rect.center().x() - totalWidth / 2;
            break;
        case Qt::AlignRight:
            baseX = rect.right() - totalWidth;
            break;
        default: // Qt::AlignLeft
            baseX = rect.left();
            break;
    }

    // Handle vertical alignment (baseline positioning is key here)
    Qt::Alignment vAlign = option.alignment() & Qt::AlignVertical_Mask;
    const auto &firstMetrics = metricsList.first();
    switch (vAlign) {
        case Qt::AlignVCenter:
            baseY = rect.center().y() + (maxHeight / 2) - firstMetrics.descent();
            break;
        case Qt::AlignBottom:
            baseY = rect.bottom() - firstMetrics.descent();
            break;
        default: // Qt::AlignTop
            baseY = rect.top() + firstMetrics.ascent();
            break;
    }

    // Draw each text segment
    painter->save();
    qreal currentX = baseX;
    for (int i = 0; i < textFragments.size(); ++i) {
        const auto &fragment = textFragments[i];
        const auto &metrics = metricsList[i];
        painter->setFont(fragment.second);
        painter->drawText(QPointF(currentX, baseY), fragment.first);
        currentX += metrics.horizontalAdvance(fragment.first);
    }
    painter->restore();
}

Usage in paintEvent

void MyWidget::paintEvent(QPaintEvent *)
{
    QPainter painter(this);
    QRectF drawRect(50, 50, 300, 100);
    painter.drawRect(drawRect); // Draw rectangle to visualize alignment

    // Define mixed-font text segments
    QList<QPair<QString, QFont>> fragments;
    fragments.append(qMakePair(QString("Hello "), QFont("Arial", 12)));
    fragments.append(qMakePair(QString("World"), QFont("Times New Roman", 12, QFont::Bold)));

    // Use center alignment (matches your original use case)
    QTextOption option(Qt::AlignCenter);
    drawMultiFontText(&painter, drawRect, fragments, option);
}

Method 2: Use QTextDocument (Best for Complex Formatting/Multi-Line Text)

If you need multi-line text, additional formatting (like colors or bold), or just cleaner code, QTextDocument is the way to go. It natively supports HTML-style formatting and honors QTextOption alignment settings.

Code Example

#include <QPainter>
#include <QTextDocument>

void drawRichTextWithAlignment(QPainter *painter, const QRectF &rect, 
                               const QString &richText, 
                               const QTextOption &option = QTextOption())
{
    QTextDocument doc;
    doc.setHtml(richText);
    doc.setTextOption(option);
    doc.setPageSize(rect.size());

    painter->save();
    painter->translate(rect.topLeft());
    doc.drawContents(painter);
    painter->restore();
}

Usage in paintEvent

void MyWidget::paintEvent(QPaintEvent *)
{
    QPainter painter(this);
    QRectF drawRect(50, 50, 300, 100);
    painter.drawRect(drawRect);

    // Define rich text with mixed fonts
    QString richText = "<span style='font-family: Arial; font-size: 12pt;'>Hello </span>"
                       "<span style='font-family: Times New Roman; font-size: 12pt; font-weight: bold;'>World</span>";

    QTextOption option(Qt::AlignCenter);
    drawRichTextWithAlignment(&painter, drawRect, richText, option);
}

Which Method Should You Choose?

  • Manual Calculation: Opt for this if you’re working with simple single-line text, need pixel-perfect control, or want minimal performance overhead.
  • QTextDocument: Use this for multi-line text, complex formatting, or when you want to avoid the boilerplate of manual position math.

内容的提问来源于stack exchange,提问作者A. Kviat

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.21 07:54:45